表示异步套接字操作。
System.EventArgs
System.Net.Sockets.SocketAsyncEventArgs
程序集: System(在 System.dll 中)
public class SocketAsyncEventArgs : EventArgs, IDisposable
SocketAsyncEventArgs 类型公开以下成员。
名称 | 说明 | |
---|---|---|
SocketAsyncEventArgs | 创建一个空的 SocketAsyncEventArgs 实例。 |
名称 | 说明 | |
---|---|---|
AcceptSocket | 获取或设置要使用的套接字或创建用于接受与异步套接字方法的连接的套接字。 | |
Buffer | 获取要用于异步套接字方法的数据缓冲区。 | |
BufferList | 获取或设置一个要用于异步套接字方法的数据缓冲区数组。 | |
BytesTransferred | 获取在套接字操作中传输的字节数。 | |
ConnectByNameError | 获取在使用 DnsEndPoint 时发生连接失败所引发的异常。 | |
ConnectSocket | 成功完成 ConnectAsync 方法后创建和连接的Socket 对象。 | |
Count | 获取可在异步操作中发送或接收的最大数据量(以字节为单位)。 | |
DisconnectReuseSocket | 获取或设置一个值,该值指定套接字能否在断开操作之后重用。 | |
LastOperation | 获取最近使用此上下文对象执行的套接字操作类型。 | |
Offset | 获取 Buffer 属性引用的数据缓冲区的偏移量(以字节为单位)。 | |
ReceiveMessageFromPacketInfo | 获取接收的数据包的 IP 地址和接口。 | |
RemoteEndPoint | 获取或设置异步操作的远程 IP 终结点。 | |
SendPacketsElements | 获取或设置要为 Socket.SendPacketsAsync 方法使用的异步操作发送的缓冲区数组。 | |
SendPacketsFlags | 为 Socket.SendPacketsAsync 方法使用的异步操作获取或设置TransmitFileOptions 值的按位组合。 | |
SendPacketsSendSize | 获取或设置发送操作中使用的数据块的大小(以字节为单位)。 | |
SocketError | 获取或设置异步套接字操作的结果。 | |
SocketFlags | 获取异步套接字操作的结果或设置异步操作的行为。 | |
UserToken | 获取或设置与此异步套接字操作关联的用户或应用程序对象。 |
名称 | 说明 | |
---|---|---|
Dispose | 释放由 SocketAsyncEventArgs 实例使用的非托管资源,并可选择释放托管资源。 | |
Equals(Object) | 确定指定的 Object 是否等于当前的Object。 (继承自Object。) | |
Finalize | SocketAsyncEventArgs 类使用的可用资源。 (重写Object.Finalize()。) | |
GetHashCode | 用作特定类型的哈希函数。 (继承自 Object。) | |
GetType | 获取当前实例的 Type。 (继承自Object。) | |
MemberwiseClone | 创建当前 Object 的浅表副本。 (继承自Object。) | |
OnCompleted | 表示异步操作完成时调用的方法。 | |
SetBuffer(Int32, Int32) | 设置要用于异步套接字方法的数据缓冲区。 | |
SetBuffer(Byte[], Int32, Int32) | 设置要用于异步套接字方法的数据缓冲区。 | |
ToString | 返回表示当前对象的字符串。 (继承自 Object。) |
名称 | 说明 | |
---|---|---|
Completed | 用于完成异步操作的事件。 |
System.Net.Sockets.Socket 类有一组增强功能,提供可供专用的高性能套接字应用程序使用的可选异步模式,SocketAsyncEventArgs 类就是这一组增强功能的一部分。 该类专为需要高性能的网络服务器应用程序而设计。应用程序可以完全使用增强的异步模式,也可以仅仅在目标热点区域(例如,在接收大量数据时)使用此模式。
这些增强功能的主要特点是可以避免在异步套接字 I/O 量非常大时发生重复的对象分配和同步。当前由 System.Net.Sockets.Socket 类实现的开始/结束设计模式要求为每个异步套接字操作分配一个System.IAsyncResult 对象。
在新的 System.Net.Sockets.Socket 类增强功能中,异步套接字操作由分配的可重用SocketAsyncEventArgs 对象描述并由应用程序维护。高性能套接字应用程序非常清楚地知道必须保持的重叠的套接字操作的量。 应用程序可以根据自身需要创建任意多的 SocketAsyncEventArgs 对象。例如,如果服务器应用程序需要总是有 15 个未完成的套接字接收操作来支持传入客户端连接率,那么可以为此分配 15 个可重用的SocketAsyncEventArgs 对象。
使用此类执行异步套接字操作的模式包含以下步骤:
-
分配一个新的 SocketAsyncEventArgs 上下文对象,或者从应用程序池中获取一个空闲的此类对象。
-
将该上下文对象的属性设置为要执行的操作(例如,完成回调方法、数据缓冲区、缓冲区偏移量以及要传输的最大数据量)。
-
调用适当的套接字方法 (xxxAsync) 以启动异步操作。
-
如果异步套接字方法 (xxxAsync) 返回 true,则在回调中查询上下文属性来获取完成状态。
-
如果异步套接字方法 (xxxAsync) 返回 false,则说明操作是同步完成的。可以查询上下文属性来获取操作结果。
-
将该上下文重用于另一个操作,将它放回到应用程序池中,或者将它丢弃。
新的异步套接字操作上下文对象的生命周期由应用程序代码引用和异步 I/O 引用决定。在对异步套接字操作上下文对象的引用作为一个参数提交给某个异步套接字操作方法之后,应用程序不必保留该引用。在完成回调返回之前将一直引用该对象。 但是,应用程序保留对上下文的引用是有好处的,这样该引用就可以重用于将来的异步套接字操作。
下面的代码示例实现使用 SocketAsyncEventArgs 类的套接字服务器的连接逻辑。接受连接之后,从客户端读取的所有数据都将发回客户端。 客户端模式的读取和回送会一直继续到客户端断开连接。 此示例使用的 BufferManager 类显示在SetBuffer(Byte[], Int32, Int32) 方法的代码示例中。此示例使用的 SocketAsyncEventArgsPool 类显示在 SocketAsyncEventArgs 构造函数的代码示例中。
// Implements the connection logic for the socket server. // After accepting a connection, all data read from the client // is sent back to the client. The read and echo back to the client pattern // is continued until the client disconnects. class Server { private int m_numConnections; // the maximum number of connections the sample is designed to handle simultaneously private int m_receiveBufferSize;// buffer size to use for each socket I/O operation BufferManager m_bufferManager; // represents a large reusable set of buffers for all socket operations const int opsToPreAlloc = 2; // read, write (don't alloc buffer space for accepts) Socket listenSocket; // the socket used to listen for incoming connection requests // pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations SocketAsyncEventArgsPool m_readWritePool; int m_totalBytesRead; // counter of the total # bytes received by the server int m_numConnectedSockets; // the total number of clients connected to the server Semaphore m_maxNumberAcceptedClients; // Create an uninitialized server instance. // To start the server listening for connection requests // call the Init method followed by Start method // // <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param> // <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param> public Server(int numConnections, int receiveBufferSize) { m_totalBytesRead = 0; m_numConnectedSockets = 0; m_numConnections = numConnections; m_receiveBufferSize = receiveBufferSize; // allocate buffers such that the maximum number of sockets can have one outstanding read and //write posted to the socket simultaneously m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc, receiveBufferSize); m_readWritePool = new SocketAsyncEventArgsPool(numConnections); m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); } // Initializes the server by preallocating reusable buffers and // context objects. These objects do not need to be preallocated // or reused, but it is done this way to illustrate how the API can // easily be used to create reusable objects to increase server performance. // public void Init() { // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds // against memory fragmentation m_bufferManager.InitBuffer(); // preallocate pool of SocketAsyncEventArgs objects SocketAsyncEventArgs readWriteEventArg; for (int i = 0; i < m_numConnections; i++) { //Pre-allocate a set of reusable SocketAsyncEventArgs readWriteEventArg = new SocketAsyncEventArgs(); readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); readWriteEventArg.UserToken = new AsyncUserToken(); // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object m_bufferManager.SetBuffer(readWriteEventArg); // add SocketAsyncEventArg to the pool m_readWritePool.Push(readWriteEventArg); } } // Starts the server such that it is listening for // incoming connection requests. // // <param name="localEndPoint">The endpoint which the server will listening // for connection requests on</param> public void Start(IPEndPoint localEndPoint) { // create the socket which listens for incoming connections listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); listenSocket.Bind(localEndPoint); // start the server with a listen backlog of 100 connections listenSocket.Listen(100); // post accepts on the listening socket StartAccept(null); //Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount); Console.WriteLine("Press any key to terminate the server process...."); Console.ReadKey(); } // Begins an operation to accept a connection request from the client // // <param name="acceptEventArg">The context object to use when issuing // the accept operation on the server's listening socket</param> public void StartAccept(SocketAsyncEventArgs acceptEventArg) { if (acceptEventArg == null) { acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed); } else { // socket must be cleared since the context object is being reused acceptEventArg.AcceptSocket = null; } m_maxNumberAcceptedClients.WaitOne(); bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { ProcessAccept(acceptEventArg); } } // This method is the callback method associated with Socket.AcceptAsync // operations and is invoked when an accept operation is complete // void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) { ProcessAccept(e); } private void ProcessAccept(SocketAsyncEventArgs e) { Interlocked.Increment(ref m_numConnectedSockets); Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", m_numConnectedSockets); // Get the socket for the accepted client connection and put it into the //ReadEventArg object user token SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; // As soon as the client is connected, post a receive to the connection bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); if(!willRaiseEvent){ ProcessReceive(readEventArgs); } // Accept the next connection request StartAccept(e); } // This method is called whenever a receive or send operation is completed on a socket // // <param name="e">SocketAsyncEventArg associated with the completed receive operation</param> void IO_Completed(object sender, SocketAsyncEventArgs e) { // determine which type of operation just completed and call the associated handler 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"); } } // This method is invoked when an asynchronous receive operation completes. // If the remote host closed the connection, then the socket is closed. // If data was received then the data is echoed back to the client. // private void ProcessReceive(SocketAsyncEventArgs e) { // check if the remote host closed the connection AsyncUserToken token = (AsyncUserToken)e.UserToken; if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) { //increment the count of the total bytes receive by the server Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred); Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead); //echo the data received back to the client e.SetBuffer(e.Offset, e.BytesTransferred); bool willRaiseEvent = token.Socket.SendAsync(e); if (!willRaiseEvent) { ProcessSend(e); } } else { CloseClientSocket(e); } } // This method is invoked when an asynchronous send operation completes. // The method issues another receive on the socket to read any additional // data sent from the client // // <param name="e"></param> private void ProcessSend(SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success) { // done echoing data back to the client AsyncUserToken token = (AsyncUserToken)e.UserToken; // read the next block of data send from the client bool willRaiseEvent = token.Socket.ReceiveAsync(e); if (!willRaiseEvent) { ProcessReceive(e); } } else { CloseClientSocket(e); } } private void CloseClientSocket(SocketAsyncEventArgs e) { AsyncUserToken token = e.UserToken as AsyncUserToken; // close the socket associated with the client try { token.Socket.Shutdown(SocketShutdown.Send); } // throws if client process has already closed catch (Exception) { } token.Socket.Close(); // decrement the counter keeping track of the total number of clients connected to the server 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); // Free the SocketAsyncEventArg so they can be reused by another client m_readWritePool.Push(e); } }
.NET Framework
受以下版本支持:4、3.5 SP1、3.0 SP1、2.0 SP1.NET Framework Client Profile
受以下版本支持:4、3.5 SP1Windows 7, Windows Vista SP1 或更高版本, Windows XP SP3, Windows Server 2008(不支持服务器核心), Windows Server 2008 R2(支持 SP1 或更高版本的服务器核心), Windows Server 2003 SP2
.NET Framework 并不是对每个平台的所有版本都提供支持。有关支持的版本的列表,请参见.NET Framework 系统要求。