一、SocketAsyncEventArgs介绍
SocketAsyncEventArgs是微软提供的高性能异步Socket实现类,主要为高性能网络服务器应用程序而设计,主要是为了避免在在异步套接字 I/O 量非常大时发生重复的对象分配和同步。使用此类执行异步套接字操作的模式包含以下步骤:
1.分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象。
2.将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。
3.调用适当的套接字方法 (xxxAsync) 以启动异步操作。
4.如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。
5.如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。 可以查询上下文属性来获取操作结果。
6.将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。
二、具体实现
1、接收连接
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnectedCompleted);
}
else
{
acceptEventArg.AcceptSocket = null;
}
maxClient.WaitOne();
if (!socket.AcceptAsync(acceptEventArg))
{
ProcessAccept(acceptEventArg);
}
}
//远端地址连接本地成功的回调
public void OnConnectedCompleted(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
}
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();
Console.WriteLine(string.Format("远程地址[{0}]成功连接到本地ip{1}", ipRemote, ip));
SocketAsyncEventArgs sendArgs = new SocketAsyncEventArgs();
sendArgs.RemoteEndPoint = token.UserSocket.RemoteEndPoint;
sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
sendArgs.UserToken = saea.UserToken;
dicSAEA.TryAdd(token.UserSocket.RemoteEndPoint.ToString(), sendArgs);
if (!e.AcceptSocket.ReceiveAsync(saea))
{
OnReceiveCompleted(saea);
}
StartAccept(e);
}
2、接收客户端信息
private void OnReceiveCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
CloseClientSocket(e);
Console.WriteLine("断开连接");
return;
}
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 <= 0)
{
msg.msgStr = "远端服务器已经断开连接";
msg.ip = ipAddress;
Console.WriteLine(msg.msgStr);
CloseClientSocket(e);
}
else
{
byte[] receiveBuffer = e.Buffer;
byte[] buffer = new byte[lengthBuffer];
Buffer.BlockCopy(receiveBuffer, 0, buffer, 0, lengthBuffer);
msg.msgStr = Encoding.UTF8.GetString(buffer);
msg.ip = ipAddress;
if (!token.UserSocket.ReceiveAsync(e))//继续异步接收消息
{
OnReceiveCompleted(e);
}
Console.WriteLine("接收消息:" + msg.msgStr);
SendAsync(msg); //测试接收消息后立即返回给客户端
}
}