SocketAsyncEventArgs是一个套接字操作的类,主要作用是实现socket消息的异步接收和发送,跟Socket的BeginSend和
BeginReceive方法异步处理没有多大区别,它的优势在于完成端口的实现来处理大数据的并发情况,由于本人学习不久,对千万级的

数据访问还没有多大体会,这里的简单实现作为一个学习的笔记,请酌情参考,如有错误,请及时指正。

先说说SockeAsyncEventArgs类的操作方法,以下是摘自MSDN的内容(cn/library/system.net.sockets.socketasynceventargs.aspx">MSDN的SockeAsyncEventArgs类描述):

1、分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象。

   SocketAsyncEventArgs saea = new SocketAsyncEventArgs();
  //或者(这里的SocketAsyncEventArgsPool类一般是自己实现,MSDN有通过栈结构实现的程序池,也可以使用队列或链表):
  SocketAsyncEventArgs saea = new SocketAsyncEventArgsPool().Pop();

2、将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。SocketAsyncEventArgs一般会根据操作执行相同的回调函数,所有设置内容在不同操作的回调都可以访问,我在调试时发现不能同时收发消息(可能是半双工),因此使用接收和发送试用两个对象

   byte[] buffer = new byte[];
  saea.SetBuffer(buffer, , buffer.Length); //设置缓冲区
  saea.Completed += new EventHandler<SocketAsyncEventArgs>(MethodName); //设置回调方法
  saea.RemoteEndPoint = new IPEndPoint(IPAddress.Any,); //设置远端连接节点,一般用于接收消息
  saea.UserToken = new AsyncUserToken(); //设置用户信息,一般把连接的Socket对象放在这里

3、调用适当的套接字方法 (xxxAsync) 以启动异步操作。
4、如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。
5、如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。 可以查询上下文属性来获取操作结果。

   //这是调用套接字的方法,即socket调用的方法:
  Socket socket = saea.AcceptSocket;
  socket.ConnectAsync(saea); //异步进行连接
  socket.AcceptAsync(saea); //异步接收连接
  socket.ReceiveAsync(saea); //异步接收消息
  socket.SendAsync(saea); //异步发送消息
  //这里注意的是,每个操作方法返回的是布尔值,这个布尔值的作用,是表明当前操作是否有等待I/O的情况,如果返回false则表示当前是同步操作,不需要等待,此时要要同步执行回调方法,一般写法是
  bool willRaiseEvent = socket.ReceiveAsync(saea); //继续异步接收消息
  if (!willRaiseEvent)
  {
    MethodName(saea);
  }

6、将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。
  如果用于持续监听连接,要注意saea.AcceptSocket = null;只有把saea对象的AcceptSocket置为null,才能监听到新的连接;
  如果只用于单次通讯,则在用完saea对象是可丢弃,saea.Dispose(),如果想重复利用,则设置相应的异步操作即可,
  

 saea.AcceptSocket = null;//重新监听
socket.ReceiveAsync(saea);//重新接收
socket.SendAsync(saea);//重新发送

关于具体的实现,类似于之前我记下的简单Socket通信,先是SocketServerManager的实现,该实现也是参考自MSDN:

     public class SocketServerManager
{
readonly Socket _socket; //监听Socket
readonly EndPoint _endPoint;
private const int Backlog = ; //允许连接数目
private int byteSize = ; //同时UI界处理的事件
public delegate void OnEventCompletedHanlder(MessageFormat msg);
public event OnEventCompletedHanlder OnReceiveCompletedEvent;
public event OnEventCompletedHanlder OnSendCompletedEvent;
public event OnEventCompletedHanlder OnConnectedEvent;
public event OnEventCompletedHanlder OnDisconnectEvent;
public event OnEventCompletedHanlder OnNotConnectEvent; //private BufferManager bufferManager; //消息缓存管理
SocketAsyncEventArgsPool rwPool; //SAEA池
private Semaphore maxClient;
private Dictionary<string, SocketAsyncEventArgs> dicSAEA = null; public SocketServerManager(string ip, int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(ip);
_endPoint = new IPEndPoint(ipAddress, port);
//bufferManager = new BufferManager(totalBytes, byteSize);
maxClient = new Semaphore(Backlog, Backlog);
Init();
} public void Init()
{
//bufferManager.InitBuffer();
SocketAsyncEventArgs rwEventArgs;
rwPool = new SocketAsyncEventArgsPool(Backlog);
dicSAEA = new Dictionary<string, SocketAsyncEventArgs>();
for (int i = ; i < ; i++)
{
rwEventArgs = new SocketAsyncEventArgs();
rwEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
rwEventArgs.UserToken = new AsyncUserToken(); rwEventArgs.SetBuffer(new byte[byteSize],,byteSize);
//bufferManager.SetBuffer(rwEventArgs); rwPool.Push(rwEventArgs);
}
} /// <summary>
/// 开启Socket监听
/// </summary>
public void Start()
{
_socket.Bind(_endPoint); //绑定本地地址进行监听
_socket.Listen(Backlog); //设置监听数量 StartAccept(null);
} public void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnectedCompleted);
}
else
{
acceptEventArg.AcceptSocket = null;
} maxClient.WaitOne();
bool willRaiseEvent = _socket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
ProcessAccept(acceptEventArg);
}
} private void ProcessAccept(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success) return; //异步处理失败,不做处理
SocketAsyncEventArgs saea = rwPool.Pop();
AsyncUserToken token = saea.UserToken as AsyncUserToken;
token.UserSocket = e.AcceptSocket; //获取远端对话Socket对象
string ipRemote = token.UserSocket.RemoteEndPoint.ToString();
string ip = token.UserSocket.RemoteEndPoint.ToString();
MessageFormat msg = new MessageFormat(string.Format("远程地址[{0}]成功连接到本地", ipRemote), ip, MsgType.Empty);
msg.tag = MsgType.Empty;
if (OnConnectedEvent != null) OnConnectedEvent(msg); //调用UI方法处理 //连接成功后,发送消息通知远程客户端
//OnSend("Connected Success !", _sendSocket.RemoteEndPoint);
SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
sendArgs.RemoteEndPoint = token.UserSocket.RemoteEndPoint;
sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
sendArgs.UserToken = saea.UserToken;
dicSAEA.Add(token.UserSocket.RemoteEndPoint.ToString(), sendArgs);
bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(saea);
if (!willRaiseEvent)
{
OnReceiveCompleted(saea);
} StartAccept(e);
} /// <summary>
/// 远端地址连接本地成功的回调
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnConnectedCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
} public void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
OnReceiveCompleted(e);
break;
case SocketAsyncOperation.Send:
OnSendCompleted(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
} /// <summary>
/// 执行异步发送消息
/// </summary>
/// <param name="msg">消息内容</param>
/// <param name="ip">发送远端地址</param>
public void OnSend(MessageFormat mf)
{
if (!dicSAEA.ContainsKey(mf.ipStr))
{
if (OnNotConnectEvent != null)
{
OnNotConnectEvent(new MessageFormat("不存在此连接客户端","",MsgType.Empty));
return;
}
}
SocketAsyncEventArgs saea = dicSAEA[mf.ipStr];
AsyncUserToken token = saea.UserToken as AsyncUserToken;
if (saea == null) return;
//saea.SetBuffer(sendBuffer, 0, sendBuffer.Length); //设置SAEA的buffer消息内容
byte[] sendBuffer = Encoding.Unicode.GetBytes(string.Format("[length={0}]{1}", mf.msgStr.Length, mf.msgStr));
saea.SetBuffer(sendBuffer, , sendBuffer.Length);
bool willRaiseEvent = token.UserSocket.SendAsync(saea);
if (!willRaiseEvent)
{
OnSendCompleted(saea);
}
} /// <summary>
/// 发送消息回调处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnSendCompleted(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken;
byte[] sendBuffer = e.Buffer;
string msgStr = Encoding.Unicode.GetString(sendBuffer);
string ipAddress = token.UserSocket.RemoteEndPoint.ToString();
MessageFormat msg = new MessageFormat(msgStr, ipAddress, MsgType.Send);
if (OnSendCompletedEvent != null) OnSendCompletedEvent(msg); //调用UI方法处理
} /// <summary>
/// 接收消息回调处理
/// </summary>
/// <param name="e"></param>
public void OnReceiveCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success) return; //判断消息的接收状态
AsyncUserToken token = e.UserToken as AsyncUserToken;
int lengthBuffer = e.BytesTransferred; //获取接收的字节长度
string ipAddress = token.UserSocket.RemoteEndPoint.ToString();
MessageFormat msg = new MessageFormat();
//如果接收的字节长度为0,则判断远端服务器关闭连接
if (lengthBuffer <= )
{
msg.msgStr = "远端服务器已经断开连接";
msg.ipStr = ipAddress;
msg.tag = MsgType.Handler;
if (OnDisconnectEvent != null) OnDisconnectEvent(msg);
CloseClientSocket(e);
}
else
{
byte[] receiveBuffer = e.Buffer;
byte[] buffer = new byte[lengthBuffer];
Buffer.BlockCopy(receiveBuffer, , buffer, , lengthBuffer);
msg.msgStr = Encoding.Unicode.GetString(buffer);
msg.ipStr = ipAddress;
msg.tag = MsgType.Receive;
bool willRaiseEvent = token.UserSocket.ReceiveAsync(e); //继续异步接收消息
if (!willRaiseEvent)
{
OnReceiveCompleted(e);
}
if (OnReceiveCompletedEvent != null) OnReceiveCompletedEvent(msg); //调用UI方法处理
}
} private void CloseClientSocket(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken;
try
{
token.UserSocket.Shutdown(SocketShutdown.Send);
}
catch (Exception) { }
dicSAEA.Remove(token.UserSocket.RemoteEndPoint.ToString());
token.UserSocket.Close(); maxClient.Release();
rwPool.Push(e);
}
}

public class SocketServerManager

通过一个栈结构SocketAsyncEventArgsPool保存的SocketAsyncEventArgs对象是用于接收消息,为了做到双方通讯,我每次在接收到远端客户端连接时,就创建一个新的SocketAsyncEventArgs对象保存在Dictionary结构中,这样在消息发送是就可以根据Ip来发送给远程客户端。

客户端的实现比较简单,不用考虑多方的通讯:

     public class SocketClientManager
{
readonly Socket _socket; //用于消息交互的socket对象
readonly EndPoint _endPoint; //远端地址
readonly SocketAsyncEventArgs _saea; //处理连接和接收SAEA对象
//处理发送的SAEA处理,由于绑定不同的回调函数,因此需要不同的SAEA对象
SocketAsyncEventArgs _sendSaea; //处理UI的事件
public delegate void OnEventCompletedHanlder(MessageFormat msgFormat);
public event OnEventCompletedHanlder OnConnectedEvent; //连接成功事件
public event OnEventCompletedHanlder OnReceiveCompletedEvent; //收到消息事件
public event OnEventCompletedHanlder OnSendCompletedEvent; //发送成功事件 public SocketClientManager(string ip, int port)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAddress = IPAddress.Parse(ip);
_endPoint = new IPEndPoint(ipAddress, port);
_saea = new SocketAsyncEventArgs {RemoteEndPoint = _endPoint};
} /// <summary>
/// 开启进行远程连接
/// </summary>
public void Start()
{
_saea.Completed += OnConnectedCompleted;
_socket.ConnectAsync(_saea); //进行异步连接 } /// <summary>
/// 连接成功的事件回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnConnectedCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success) return;
Socket socket = sender as Socket;
string ipRemote = socket.RemoteEndPoint.ToString();
MessageFormat messageFormat = new MessageFormat(string.Format("连接服务器[{0}]成功!", ipRemote), socket.LocalEndPoint.ToString(), MsgType.Empty);
if (OnConnectedEvent != null) OnConnectedEvent(messageFormat); //开启新的接受消息异步操作事件
var receiveSaea = new SocketAsyncEventArgs();
var receiveBuffer = new byte[ * ];
receiveSaea.SetBuffer(receiveBuffer, , receiveBuffer.Length); //设置消息的缓冲区大小
receiveSaea.Completed += OnReceiveCompleted; //绑定回调事件
receiveSaea.RemoteEndPoint = _endPoint;
_socket.ReceiveAsync(receiveSaea);
} /// <summary>
/// 接受消息的回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.OperationAborted) return;
var socket = sender as Socket;
MessageFormat messageFormat = new MessageFormat();
if (e.SocketError == SocketError.Success &&e.BytesTransferred > )
{
string ipAddress = socket.RemoteEndPoint.ToString();
int lengthBuffer = e.BytesTransferred;
byte[] receiveBuffer = e.Buffer;
byte[] buffer = new byte[lengthBuffer];
Buffer.BlockCopy(receiveBuffer, , buffer, , lengthBuffer);
messageFormat.msgStr = Encoding.Unicode.GetString(buffer);
messageFormat.ipStr = ipAddress;
messageFormat.tag = MsgType.Receive;;
socket.ReceiveAsync(e);
}
else if (e.SocketError == SocketError.ConnectionReset && e.BytesTransferred == )
{
messageFormat.msgStr = "服务器已经断开连接";
messageFormat.ipStr = socket.RemoteEndPoint.ToString();
messageFormat.tag = MsgType.Handler;
}
else
{
return;
}
if (OnReceiveCompletedEvent != null) OnReceiveCompletedEvent(messageFormat);
} /// <summary>
/// 发送消息回调函数
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void OnSendCompleted(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success) return;
var socket = sender as Socket;
byte[] sendBuffer = e.Buffer;
MessageFormat messageFormat = new MessageFormat(Encoding.Unicode.GetString(sendBuffer),socket.RemoteEndPoint.ToString(),MsgType.Send);
if (OnSendCompletedEvent != null) OnSendCompletedEvent(messageFormat);
} /// <summary>
/// 断开连接
/// </summary>
public void OnDisConnect()
{
if (_socket != null)
{
try
{
_socket.Shutdown(SocketShutdown.Both);
}
catch (SocketException ex)
{
}
finally
{
_socket.Close();
}
}
} /// <summary>
/// 发送消息
/// </summary>
/// <param name="msg"></param>
public void SendMsg(MessageFormat mf)
{
byte[] sendBuffer = Encoding.Unicode.GetBytes(string.Format("[length={0}]{1}", mf.msgStr.Length, mf.msgStr));
if (_sendSaea == null)
{
_sendSaea = new SocketAsyncEventArgs {RemoteEndPoint = _endPoint};
_sendSaea.Completed += OnSendCompleted;
}
_sendSaea.SetBuffer(sendBuffer, , sendBuffer.Length);
if (_socket != null) _socket.SendAsync(_sendSaea);
}
}

public class SocketClientManager

同样是使用收发不同的SocketAsyncEventArgs对象。

另外,关于解决缓存容量不足以容纳一条消息的半包问题,这里使用了简单的字符处理类,这个类是复制自Jimmy Zhang的类RequestHandler,当然,方法还是不完善的,难以解决不同顺序的消息接受。

具体源码(.net4.5,vs2013):具体源码 http://files.cnblogs.com/files/supheart/ServerBySocket.zip

C#使用SocketAsyncEventArgs操作套接字的简单异步通讯的更多相关文章

  1. IDEA Spark Streaming 操作(套接字流)

    import org.apache.spark.SparkConf import org.apache.spark.streaming.{Seconds, StreamingContext} obje ...

  2. Python之路(第三十二篇) 网络编程:udp套接字、简单文件传输

    一.UDP套接字 服务端 # udp是无链接的,先启动哪一端都不会报错 # udp没有链接,与tcp相比没有链接循环,只有通讯循环 server = socket.socket(socket.AF_I ...

  3. 原始套接字的简单tcp包嗅探

    原始套接字 sock_raw = socket(AF_INET , SOCK_RAW , IPPROTO_TCP); while(1) { data_size = recvfrom(sock_raw ...

  4. UDP—Socket,套接字聊天简单的聊天程序。

    思路:(发送端) 1.既然需要聊天.就应该怎么建立聊天程序,,DatagramSocket对象http://www.w3cschool.cc/manual/jdk1.6/ DatagramSocket ...

  5. IDEA Spark Streaming 操作(套接字流)-----make socket数据源

    import java.io.PrintWriter import java.net.ServerSocket import scala.io.Source object DStream_makeSo ...

  6. VBox虚拟机与主机(宿主)通讯原理以及socat(套接字猫)简单介绍

    前言 尝试虚拟机使用socat建立服务器端接口转发时,发现对虚拟机接入网络原理不是非常了解,于是乎上网查找资料想搞明白是怎么回事,于是乎有了这篇总结博文.socat可以在服务器端口间建立全双工通信通道 ...

  7. ZeroMQ接口函数之 :zmq_socket – 创建ZMQ套接字

    ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ZeroMQ 官方地址:http://api.zeromq.org/4 ...

  8. <unix网络编程>UDP套接字编程

    典型的UDP客户/服务器程序的函数调用如下: 1.缓冲区 发送缓冲区用虚线表示,任何UDP套接字都有发送缓冲区,不过该缓冲区仅能表示写到该套接字的UDP数据报的上限.如果应用进程写一个大于套接字缓冲区 ...

  9. Linux套接字和I/O模型

    目录 1.       socket套接字的属性.地址和创建 2.       如何使用socket编写简单的同步阻塞的服务器/客户端 3.       理解Linux五种I/O模型 1.socket ...

随机推荐

  1. Linux编程之《只运行一个实例》

    概述 有些时候,我们要求一个程序在系统中只能启动一个实例.比如,Windows自带的播放软件Windows Medea Player在Windows里就只能启动一个实例.原因很简单,如果同时启动几个实 ...

  2. 1.7.4.1 Function Queries-函数查询

    1 . Function Queries 函数查询使你可以使用一个或者多个数字字段的实际的值生成一个关联的得分(score),函数查询支持DixMax,eDisMax,标准的查询解析. 函数查询使用函 ...

  3. [super dealloc]内存释放的先后顺序

    心得:从前做内存释放,只是觉得应该,没体会到这个的重要性,如果不及时释放就会有很多内存泄露,就像我早期遇到的前赴后继的崩溃,比如:没使用完,就释放会崩溃等明显的release问题.     作为全局的 ...

  4. ReSharper 8.0.2000.2660

    user:dobit sn:G/YgFyekI7EL0oBc5YBWKI5WCi3pwXWP 下载地址

  5. 3. Android框架和工具之 xUtils(ViewUtils )

    1. ViewUtils 作用: 完全注解方式就可以进行UI绑定和事件绑定. 无需findViewById和setClickListener等. 2. UI绑定 和 事件绑定 (1)UI绑定 下面我們 ...

  6. ios优化复制大文件时,如何使内存运用最少且效率最高

    我也是纠结了好几天,我想自己想个办法,但是数据复制不上去,我现在还不明白,如果有人知道我错在哪了,请留言,如果还有更好的方法,请分享共同进步. ____________________________ ...

  7. Qt绘图控件qwt绘制等比例坐标图

    需要用到QwtPlotRescaler类,用法如下: QwtPlotRescaler *plotRescaler = new QwtPlotRescaler(canvas, yLeft, QwtPlo ...

  8. JavaScript 键盘event.keyCode值列表大全

      JavaScript 键盘event.keyCode值列表大全   event.keyCode值列表大全,对于需要根据键盘按键触发相应事件的朋友需要. 网上收集的KeyCode值方便大家查找: k ...

  9. python实现发送邮件功能

    '''套接字是为特定的网络协议(例如TCP/IP,ICMP/IP,UDP/IP等),允许程序和接受并进行连接,要在python 中建立具有TCP和流套接字的简单服务器,需要使用socket模块,利用该 ...

  10. 洛谷P1251 餐巾(网络流)

    P1251 餐巾 15通过 95提交 题目提供者该用户不存在 标签网络流贪心 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 为什么我全部10个测试点都对… 题目描述 一个餐厅在相继的N天里 ...