- Namespace:System.Net.Sockets
- Assemblies:System.Net.Sockets.dll, System.dll, netstandard.dll
- (Represents an asynchronous socket operation)代表一個異步套接字操作:
-
public class SocketAsyncEventArgs : EventArgs, IDisposable
- Inheritance(繼承) Object-> EventArgs-> SocketAsyncEventArgs
- Implements(實現) IDisposable
實例
下面的代碼示例實現連接邏輯使用套接字服務器socketasynceventargs類接受連接后,從客戶端讀取所有數據發送回客戶端。讀和回聲返回到客戶端模式直到客戶端斷開連接。緩沖管理器類,通過這個例子,用於在代碼示例顯示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 (用於每個套接字I/O操作的緩沖區大小) 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(可重用SocketAsyncEventArgs對象池,用於編寫、讀取和接受套接字操作) 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 //(創建一個未初始化的服務器實例。要啟動服務器偵聽連接請求,請調用init方法,然后使用start方法。) // <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(用於每個套接字I/O操作的緩沖區大小)</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. //(通過預先分配可重用的緩沖區和上下文對象初始化服務器。這些對象不需要預先分配或重用,而是通過這種方式來說明API是如何容易地被用來創建可重用對象以提高服務器性能的。) public void Init() { // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds // against memory fragmentation(分配一個大字節緩沖區,所有I/O操作都使用一個。
這是反對記憶碎片的) 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(指定從緩沖池字節緩沖區的socketasynceventarg對象) 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); } }待續