摘要:

  System.Net.Sockets.Sockte 类有一组增强功能,提供可供专用的高性能套接字应用程序使用的可选异步模式,SocketAsyncEventArgs 类就是这一组增强功能的一部分。 该类专为需要高性能的网络服务器应用程序而设计。 应用程序可以完全使用增强的异步模式,也可以仅仅在目标热点区域(例如,在接收大量数据时)使用此模式。

  这些增强功能的主要特点是可以避免在异步套接字 I/O 量非常大时发生重复的对象分配和同步。

  在新的 System.Net.Sockets.Sockte 类增强功能中,异步套接字操作由分配的可重用 SocketAsyncEventArgs 对象描述并由应用程序维护。 高性能套接字应用程序非常清楚地知道必须保持的重叠的套接字操作的量。 应用程序可以根据自身需要创建任意多的SocketAsyncEventArgs 对象。 例如,如果服务器应用程序需要总是有 15 个未完成的套接字接收操作来支持传入客户端连接率,那么可以为此分配 15 个可重用的 SocketAsyncEventArgs 对象。

  使用此类执行异步套接字操作的模式包含以下步骤:

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

  2. 将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。

  3. 调用适当的套接字方法 (xxxAsync) 以启动异步操作。

  4. 如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。

  5. 如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。 可以查询上下文属性来获取操作结果。

  6. 将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。

  新的异步套接字操作上下文对象的生命周期由应用程序代码引用和异步 I/O 引用决定。 在对异步套接字操作上下文对象的引用作为一个参数提交给某个异步套接字操作方法之后,应用程序不必保留该引用。 在完成回调返回之前将一直引用该对象。 但是,应用程序保留对上下文的引用是有好处的,这样该引用就可以重用于将来的异步套接字操作。

  下面的代码示例实现使用 SocketAsyncEventArgs 类的套接字服务器的连接逻辑。 接受连接之后,从客户端读取的所有数据都将发回客户端。 客户端模式的读取和回送会一直继续到客户端断开连接

 一、服务端 

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading; namespace ConsoleTest
{
public class Server
{
/// <summary>
/// 同时处理的最大连接数
/// </summary>
private int m_numConnections; /// <summary>
/// 每个Socket IO可使用的缓冲区大小
/// </summary>
private int m_receiveBufferSize; BufferManager m_bufferManager;
const int opsToPreAlloc = 2;
Socket listenSocket;
SocketAsyncEventArgsPool m_readWritePool; /// <summary>
/// 服务端接受的总数据大小
/// </summary>
int m_totalBytesRead; /// <summary>
/// 连接到服务端的客户端数量
/// </summary>
int m_numConnectedSockets;
Semaphore m_maxNumberAcceptedClients; public Server(int numConnections, int receiveBufferSize)
{
m_totalBytesRead = 0;
m_numConnectedSockets = 0;
m_numConnections = numConnections;
m_receiveBufferSize = receiveBufferSize;
m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc, receiveBufferSize);
m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
} public void Init()
{
m_bufferManager.InitBuffer();
SocketAsyncEventArgs readWriteEventArg;
for (int i = 0; i < m_numConnections; i++)
{
readWriteEventArg = new SocketAsyncEventArgs();
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
readWriteEventArg.UserToken = new AsyncUserToken();
m_bufferManager.SetBuffer(readWriteEventArg);
m_readWritePool.Push(readWriteEventArg);
}
} /// <summary>
/// 开始监听
/// </summary>
public void Start(IPEndPoint localEndPoint)
{
listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(localEndPoint);
listenSocket.Listen(100); //开始监听客户端连接
StartAccept(null); Console.WriteLine("按任意键终止服务器...");
Console.ReadKey();
} /// <summary>
/// 处理来自客户机的连接请求
/// </summary>
/// <param name="acceeptEventArg"></param>
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
if (acceptEventArg == null)
{
acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
}
else
{
acceptEventArg.AcceptSocket = null;
} m_maxNumberAcceptedClients.WaitOne();
//异步接收客户端连接,操作完成后触发AcceptEventArg_Completed事件
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
//如果是同步完成
if (!willRaiseEvent)
{
ProcessAccept(acceptEventArg);
}
} /// <summary>
/// 接收到数据后触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void IO_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
} /// <summary>
/// 侦听到客户端连接时触发
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
ProcessAccept(e);
} /// <summary>
///
/// </summary>
/// <param name="e"></param>
private void ProcessAccept(SocketAsyncEventArgs e)
{
Interlocked.Increment(ref m_numConnectedSockets);
Console.WriteLine("已接受客户端连接. There are {0} clients connected to the server",
m_numConnectedSockets); SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; //异步接受数据,接受完成则触发AcceptEventArg_Completed事件
bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); if (!willRaiseEvent)
{
ProcessReceive(readEventArgs);
}
//循环接受下一个客户端连接
StartAccept(e);
} private void ProcessReceive(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken;
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
Console.WriteLine("服务器已经接受 {0} 字节数据", m_totalBytesRead); string received = Encoding.Unicode.GetString(e.Buffer, e.Offset, e.BytesTransferred); for (int i = 73728; i < e.Buffer.Length; i++)
{
byte o = e.Buffer[i];
} Console.WriteLine(received); //异步发送是将Buffer空间的所有数据拷贝到基础系统发送缓冲区
e.SetBuffer(e.Offset, e.BytesTransferred);
string str1 = Encoding.Unicode.GetString(e.Buffer);
//发送数据到客户端
bool willRaiseEvent = token.Socket.SendAsync(e);
if (!willRaiseEvent)
{
ProcessSend(e);
}
}
else
{
CloseClientSocket(e);
}
} private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
AsyncUserToken token = (AsyncUserToken)e.UserToken;
bool willRaiseEvent = token.Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
} private void CloseClientSocket(SocketAsyncEventArgs e)
{
AsyncUserToken token = e.UserToken as AsyncUserToken; try
{
token.Socket.Shutdown(SocketShutdown.Send);
}
catch (Exception) { }
token.Socket.Close(); Interlocked.Decrement(ref m_numConnectedSockets); m_maxNumberAcceptedClients.Release();
Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets); m_readWritePool.Push(e);
} }
}

  

using System.Net.Sockets;

namespace ConsoleTest
{
class AsyncUserToken
{
Socket m_socket; public AsyncUserToken() : this(null) { } public AsyncUserToken(Socket socket)
{
m_socket = socket;
} public Socket Socket
{
get { return m_socket; }
set { m_socket = value; }
} }
}

  

using System.Collections.Generic;
using System.Net.Sockets; namespace ConsoleTest
{
/// <summary>
/// 创建一个大型缓冲区,缓冲区可以进行分割并指定给 SocketAsyncEventArgs 对象以便用在每个套接字 I/O 操作中
/// </summary>
public class BufferManager
{
/// <summary>
/// 缓冲区大小
/// </summary>
int m_numBytes; /// <summary>
/// BufferManager维护的底层字节数组缓冲区
/// </summary>
byte[] m_buffer; /// <summary>
/// 记录偏移量
/// </summary>
Stack<int> m_freeIndexPool; /// <summary>
/// 缓存区当前位置
/// </summary>
int m_currentIndex; /// <summary>
///
/// </summary>
int m_bufferSize; /// <summary>
/// 构造函数
/// </summary>
/// <param name="totalBytes">缓冲区字节总数</param>
/// <param name="bufferSize">要缓存的数据</param>
public BufferManager(int totalBytes, int bufferSize)
{
m_numBytes = totalBytes;
m_currentIndex = 0;
m_bufferSize = bufferSize;
m_freeIndexPool = new Stack<int>();
} /// <summary>
/// 初始化缓冲区
/// </summary>
public void InitBuffer()
{
m_buffer = new byte[m_numBytes];
} /// <summary>
/// 设置要用于异步套接字方法的数据缓冲区。
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public bool SetBuffer(SocketAsyncEventArgs args)
{
if (m_freeIndexPool.Count > 0)
{
args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
}
else
{
//缓存区大小容量不够
if ((m_numBytes - m_bufferSize) < m_currentIndex) { return false; }
args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
m_currentIndex += m_bufferSize;
}
return true;
} /// <summary>
/// 释放缓冲区
/// </summary>
/// <param name="args"></param>
public void FreeBuffer(SocketAsyncEventArgs args)
{
m_freeIndexPool.Push(args.Offset);
args.SetBuffer(null, 0, 0);
}
}
}

  

using System;
using System.Collections.Generic;
using System.Net.Sockets; namespace ConsoleTest
{
/// <summary>
/// 代表一个可重用的SocketAsyncEventArgs对象的集合。
/// </summary>
public class SocketAsyncEventArgsPool
{
Stack<SocketAsyncEventArgs> m_pool; public SocketAsyncEventArgsPool(int capacity)
{
m_pool = new Stack<SocketAsyncEventArgs>(capacity);
} /// <summary>
/// 添加SocketAsyncEventArg实例到池
/// </summary>
/// <param name="item"></param>
public void Push(SocketAsyncEventArgs item)
{
if (item == null) throw new ArgumentNullException("参数不能为空!"); lock (m_pool)
{
m_pool.Push(item);
}
} /// <summary>
/// 从池中删除SocketAsyncEventArgs实例
/// </summary>
/// <returns></returns>
public SocketAsyncEventArgs Pop()
{
lock (m_pool)
{
return m_pool.Pop();
}
} /// <summary>
/// 缓冲池中的实例个数
/// </summary>
public int Count
{
get { return m_pool.Count; }
}
}
}

  

二、客户端调用:

using System.Collections.Generic;
using System.Net.Sockets; namespace ConsoleTest
{
/// <summary>
/// 创建一个大型缓冲区,缓冲区可以进行分割并指定给 SocketAsyncEventArgs 对象以便用在每个套接字 I/O 操作中
/// </summary>
public class BufferManager
{
/// <summary>
/// 缓冲区大小
/// </summary>
int m_numBytes; /// <summary>
/// BufferManager维护的底层字节数组缓冲区
/// </summary>
byte[] m_buffer; /// <summary>
/// 记录偏移量
/// </summary>
Stack<int> m_freeIndexPool; /// <summary>
/// 缓存区当前位置
/// </summary>
int m_currentIndex; /// <summary>
///
/// </summary>
int m_bufferSize; /// <summary>
/// 构造函数
/// </summary>
/// <param name="totalBytes">缓冲区字节总数</param>
/// <param name="bufferSize">要缓存的数据</param>
public BufferManager(int totalBytes, int bufferSize)
{
m_numBytes = totalBytes;
m_currentIndex = 0;
m_bufferSize = bufferSize;
m_freeIndexPool = new Stack<int>();
} /// <summary>
/// 初始化缓冲区
/// </summary>
public void InitBuffer()
{
m_buffer = new byte[m_numBytes];
} /// <summary>
/// 设置要用于异步套接字方法的数据缓冲区。
/// </summary>
/// <param name="args"></param>
/// <returns></returns>
public bool SetBuffer(SocketAsyncEventArgs args)
{
if (m_freeIndexPool.Count > 0)
{
args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
}
else
{
//缓存区大小容量不够
if ((m_numBytes - m_bufferSize) < m_currentIndex) { return false; }
args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
m_currentIndex += m_bufferSize;
}
return true;
} /// <summary>
/// 释放缓冲区
/// </summary>
/// <param name="args"></param>
public void FreeBuffer(SocketAsyncEventArgs args)
{
m_freeIndexPool.Push(args.Offset);
args.SetBuffer(null, 0, 0);
}
}
}

  

  

Socket 异步的更多相关文章

  1. socket异步编程--libevent的使用

    使用 libevent 和 libev 提高网络应用性能 http://www.ibm.com/developerworks/cn/aix/library/au-libev/ libevent实现ht ...

  2. Socket异步发送的同步控制

    在网络通信中,我们使用Socket异步发送数据,但在客户端,往往是需要等待服务器的返回结果后(握手过程)再往下执行,这就涉及到同步控制了,在多次的实现中,使用AutoResetEvent,实现不,即有 ...

  3. [转] socket异步编程--libevent的使用

    这篇文章介绍下libevent在socket异步编程中的应用.在一些对性能要求较高的网络应用程序中,为了防止程序阻塞在socket I/O操作上造成程序性能的下降,需要使用异步编程,即程序准备好读写的 ...

  4. .net平台下socket异步通讯(代码实例)

    你应该知道的.net平台下socket异步通讯(代码实例) 1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上 ...

  5. 基于.net的Socket异步编程总结

    最近在为公司的分布式服务框架做支持异步调用的开发,这种新特性的上线需要进行各种严格的测试.在并发性能测试时,性能一直非常差,而且非常的不稳定.经过不断的分析调优,发现Socket通信和多线程异步回调存 ...

  6. 你应该知道的.net平台下socket异步通讯(代码实例)

    1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上添加TextBox控件 using System.Net; ...

  7. C# Socket异步聊天例子

    最近在配合游戏服务器端搞一个客户端通信,客户端是unity搞的,理所当然就高C#了,上手之前先看了一下C# Socket通信这一块,基本不考虑同步方式,而异步方式,微软也提供了两套API,一套是Beg ...

  8. Socket异步通讯

    1.可以通过多线程来解决(一会补上) 2.Socket在tcp/udp两种通信协议下的异步通信: 基于TCP的异步通信: BeginAccept方法和endeaccept方法 包含在System.Ne ...

  9. Socket异步存储示例

    异步客户端存储示例: using System; using System.Net; using System.Net.Sockets; using System.Threading; using S ...

  10. socket 异步选择 WSAAsyncSelect 用法

    WSAAsyncSelect 实现给异步socket给了另一种实现方式,就是通过窗口消息的方式来提醒对socket接收还是发送 msdn有非常全面的解释:https://msdn.microsoft. ...

随机推荐

  1. H5禁止手机虚拟键盘弹出

    点击输入框弹出自定义弹窗,输入框是input标:但是在移动端,input会默认触发手机的虚拟键盘,如何阻止手机虚拟键盘弹起呢?目前我试过有两个方案,一个是给input添加readonly属性,另一个就 ...

  2. mysql 如何解决字段不区分大小写的问题

    当我们输入不管大小写都能查询到数据,例如:输入 aaa 或者aaA ,AAA都能查询同样的结果,说明查询条件对大小写不敏感. 解决方案一: 于是怀疑Mysql的问题.做个实验:直接使用客户端用sql查 ...

  3. Chocolatey - Windows Software Management Automation

    What is Chocolatey? Chocolatey is a software management solution unlike anything else you've ever ex ...

  4. MyEclipse解决Launching xx on MyEclipse Tomcat has encountered a problem

    单击工具栏Run,选中Run Configurations... 将MyEclipse Server Application里面的工程右击选择Delete就好了.

  5. Chrome工具使用

    (1) Chrome插件的使用 本来还想说FQ了,结果实验半天没成功,最后才知道公司已经邮件通知了,郁闷,FQ后我把我的插件重新装了一遍,觉得好像又懂了好多,记载下来我装的一些东西和有关PostMan ...

  6. 3.redis设计与实现--字典

    1.包括三个结构体:字典结构体+哈希表结构体+哈希节点结构体 2.如何解决哈希冲突? 答:redis使用的是链地址法来解决哈希冲突的,每个链表节点有一个next指针,最新加入的节点会放在链表的头部. ...

  7. 【IIS】IIS中同时满足集成模式和经典模式

    手里有一个项目--系统设置(主要功能是对系统一些字典表的设置.权限管理等功能).在VS上运行没有任何问题.可是发布到IIS上之后,报黄页. 发布后程序运行环境为: windows 7 32位 IIS为 ...

  8. 【BZOJ4870】组合数问题(计数DP,快速幂)

    题意: 1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1 思路:From http://blog.csdn.net/qq_33229466/artic ...

  9. Go语言 7 并发编程

    文章由作者马志国在博客园的原创,若转载请于明显处标记出处:http://www.cnblogs.com/mazg/ Go学习群:415660935 今天我们学习Go语言编程的第七章,并发编程.语言级别 ...

  10. ARP投毒攻击

    原理:通过分别伪装成客户机和服务器IP,将自己的MAC地址绑定在IP上,ARP错误的将IP解析为中间人MAC地址,从而来欺骗服务器网关和客户机,使信息必须通过客户机.