SocketAsyncEvent方式的Server
1.AsyncUserToken
1 public class AsyncUserToken 2 { 3 /// <summary> 4 /// 客户端IP地址 5 /// </summary> 6 public IPAddress IPAddress { get; set; } 7 8 /// <summary> 9 /// 远程地址 10 /// </summary> 11 public EndPoint Remote { get; set; } 12 13 /// <summary> 14 /// 通信SOKET 15 /// </summary> 16 public Socket Socket { get; set; } 17 18 /// <summary> 19 /// 连接时间 20 /// </summary> 21 public DateTime ConnectTime { get; set; } 22 23 /// <summary> 24 /// 数据缓存区 25 /// </summary> 26 public List<byte> Buffer { get; set; } 27 28 public AsyncUserToken() 29 { 30 this.Buffer = new List<byte>(); 31 } 32 }
2.SocketAsyncEventArgsPool
1 public class SocketAsyncEventArgsPool 2 { 3 Stack<SocketAsyncEventArgs> m_pool; 4 5 // Initializes the object pool to the specified size 6 // 7 // The "capacity" parameter is the maximum number of 8 // SocketAsyncEventArgs objects the pool can hold 9 public SocketAsyncEventArgsPool(int capacity) 10 { 11 m_pool = new Stack<SocketAsyncEventArgs>(capacity); 12 } 13 14 // Add a SocketAsyncEventArg instance to the pool 15 // 16 //The "item" parameter is the SocketAsyncEventArgs instance 17 // to add to the pool 18 public void Push(SocketAsyncEventArgs item) 19 { 20 if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); } 21 lock (m_pool) 22 { 23 m_pool.Push(item); 24 } 25 } 26 27 // Removes a SocketAsyncEventArgs instance from the pool 28 // and returns the object removed from the pool 29 public SocketAsyncEventArgs Pop() 30 { 31 lock (m_pool) 32 { 33 return m_pool.Pop(); 34 } 35 } 36 37 // The number of SocketAsyncEventArgs instances in the pool 38 public int Count 39 { 40 get { return m_pool.Count; } 41 } 42 43 }
3.BufferManager
1 public class BufferManager 2 { 3 int m_numBytes; // the total number of bytes controlled by the buffer pool 4 byte[] m_buffer; // the underlying byte array maintained by the Buffer Manager 5 Stack<int> m_freeIndexPool; // 6 int m_currentIndex; 7 int m_bufferSize; 8 9 public BufferManager(int totalBytes, int bufferSize) 10 { 11 m_numBytes = totalBytes; 12 m_currentIndex = 0; 13 m_bufferSize = bufferSize; 14 m_freeIndexPool = new Stack<int>(); 15 } 16 17 // Allocates buffer space used by the buffer pool 18 public void InitBuffer() 19 { 20 // create one big large buffer and divide that 21 // out to each SocketAsyncEventArg object 22 m_buffer = new byte[m_numBytes]; 23 } 24 25 // Assigns a buffer from the buffer pool to the 26 // specified SocketAsyncEventArgs object 27 // 28 // <returns>true if the buffer was successfully set, else false</returns> 29 public bool SetBuffer(SocketAsyncEventArgs args) 30 { 31 32 if (m_freeIndexPool.Count > 0) 33 { 34 args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize); 35 } 36 else 37 { 38 if ((m_numBytes - m_bufferSize) < m_currentIndex) 39 { 40 return false; 41 } 42 args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize); 43 m_currentIndex += m_bufferSize; 44 } 45 return true; 46 } 47 48 // Removes the buffer from a SocketAsyncEventArg object. 49 // This frees the buffer back to the buffer pool 50 public void FreeBuffer(SocketAsyncEventArgs args) 51 { 52 m_freeIndexPool.Push(args.Offset); 53 args.SetBuffer(null, 0, 0); 54 } 55 }
4.Server
1 public class Server 2 { 3 private int m_numConnections; // the maximum number of connections the sample is designed to handle simultaneously 4 private int m_receiveBufferSize;// buffer size to use for each socket I/O operation 5 BufferManager m_bufferManager; // represents a large reusable set of buffers for all socket operations 6 const int opsToPreAlloc = 2; // read, write (don't alloc buffer space for accepts) 7 Socket listenSocket; // the socket used to listen for incoming connection requests 8 // pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations 9 SocketAsyncEventArgsPool m_readWritePool; 10 int m_totalBytesRead; // counter of the total # bytes received by the server 11 int m_numConnectedSockets; // the total number of clients connected to the server 12 Semaphore m_maxNumberAcceptedClients; 13 14 // Create an uninitialized server instance. 15 // To start the server listening for connection requests 16 // call the Init method followed by Start method 17 // 18 // <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param> 19 // <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param> 20 public Server(int numConnections, int receiveBufferSize) 21 { 22 m_totalBytesRead = 0; 23 m_numConnectedSockets = 0; 24 m_numConnections = numConnections; 25 m_receiveBufferSize = receiveBufferSize; 26 // allocate buffers such that the maximum number of sockets can have one outstanding read and 27 //write posted to the socket simultaneously 28 m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc, 29 receiveBufferSize); 30 31 m_readWritePool = new SocketAsyncEventArgsPool(numConnections); 32 m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); 33 } 34 35 // Initializes the server by preallocating reusable buffers and 36 // context objects. These objects do not need to be preallocated 37 // or reused, but it is done this way to illustrate how the API can 38 // easily be used to create reusable objects to increase server performance. 39 // 40 public void Init() 41 { 42 // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds 43 // against memory fragmentation 44 m_bufferManager.InitBuffer(); 45 46 // preallocate pool of SocketAsyncEventArgs objects 47 SocketAsyncEventArgs readWriteEventArg; 48 49 for (int i = 0; i < m_numConnections; i++) 50 { 51 //Pre-allocate a set of reusable SocketAsyncEventArgs 52 readWriteEventArg = new SocketAsyncEventArgs(); 53 readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); 54 readWriteEventArg.UserToken = new AsyncUserToken(); 55 56 // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object 57 m_bufferManager.SetBuffer(readWriteEventArg); 58 59 // add SocketAsyncEventArg to the pool 60 m_readWritePool.Push(readWriteEventArg); 61 } 62 63 } 64 65 // Starts the server such that it is listening for 66 // incoming connection requests. 67 // 68 // <param name="localEndPoint">The endpoint which the server will listening 69 // for connection requests on</param> 70 public void Start(IPEndPoint localEndPoint) 71 { 72 // create the socket which listens for incoming connections 73 listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); 74 listenSocket.Bind(localEndPoint); 75 // start the server with a listen backlog of 100 connections 76 listenSocket.Listen(100); 77 78 // post accepts on the listening socket 79 StartAccept(null); 80 81 //Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount); 82 Console.WriteLine("Press any key to terminate the server process...."); 83 Console.ReadKey(); 84 } 85 86 87 // Begins an operation to accept a connection request from the client 88 // 89 // <param name="acceptEventArg">The context object to use when issuing 90 // the accept operation on the server's listening socket</param> 91 public void StartAccept(SocketAsyncEventArgs acceptEventArg) 92 { 93 if (acceptEventArg == null) 94 { 95 acceptEventArg = new SocketAsyncEventArgs(); 96 acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed); 97 } 98 else 99 { 100 // socket must be cleared since the context object is being reused 101 acceptEventArg.AcceptSocket = null; 102 } 103 104 m_maxNumberAcceptedClients.WaitOne(); 105 bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg); 106 if (!willRaiseEvent) 107 { 108 ProcessAccept(acceptEventArg); 109 } 110 } 111 112 // This method is the callback method associated with Socket.AcceptAsync 113 // operations and is invoked when an accept operation is complete 114 // 115 void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) 116 { 117 ProcessAccept(e); 118 } 119 120 private void ProcessAccept(SocketAsyncEventArgs e) 121 { 122 Interlocked.Increment(ref m_numConnectedSockets); 123 Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", 124 m_numConnectedSockets); 125 126 // Get the socket for the accepted client connection and put it into the 127 //ReadEventArg object user token 128 SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); 129 ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; 130 131 // As soon as the client is connected, post a receive to the connection 132 bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); 133 if (!willRaiseEvent) 134 { 135 ProcessReceive(readEventArgs); 136 } 137 138 // Accept the next connection request 139 StartAccept(e); 140 } 141 142 // This method is called whenever a receive or send operation is completed on a socket 143 // 144 // <param name="e">SocketAsyncEventArg associated with the completed receive operation</param> 145 void IO_Completed(object sender, SocketAsyncEventArgs e) 146 { 147 // determine which type of operation just completed and call the associated handler 148 switch (e.LastOperation) 149 { 150 case SocketAsyncOperation.Receive: 151 ProcessReceive(e); 152 break; 153 case SocketAsyncOperation.Send: 154 ProcessSend(e); 155 break; 156 default: 157 throw new ArgumentException("The last operation completed on the socket was not a receive or send"); 158 } 159 } 160 161 // This method is invoked when an asynchronous receive operation completes. 162 // If the remote host closed the connection, then the socket is closed. 163 // If data was received then the data is echoed back to the client. 164 // 165 private void ProcessReceive(SocketAsyncEventArgs e) 166 { 167 // check if the remote host closed the connection 168 AsyncUserToken token = (AsyncUserToken)e.UserToken; 169 if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success) 170 { 171 //increment the count of the total bytes receive by the server 172 Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred); 173 Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead); 174 175 //echo the data received back to the client 176 e.SetBuffer(e.Offset, e.BytesTransferred); 177 bool willRaiseEvent = token.Socket.SendAsync(e); 178 if (!willRaiseEvent) 179 { 180 ProcessSend(e); 181 } 182 183 } 184 else 185 { 186 CloseClientSocket(e); 187 } 188 } 189 190 // This method is invoked when an asynchronous send operation completes. 191 // The method issues another receive on the socket to read any additional 192 // data sent from the client 193 // 194 // <param name="e"></param> 195 private void ProcessSend(SocketAsyncEventArgs e) 196 { 197 if (e.SocketError == SocketError.Success) 198 { 199 // done echoing data back to the client 200 AsyncUserToken token = (AsyncUserToken)e.UserToken; 201 // read the next block of data send from the client 202 bool willRaiseEvent = token.Socket.ReceiveAsync(e); 203 if (!willRaiseEvent) 204 { 205 ProcessReceive(e); 206 } 207 } 208 else 209 { 210 CloseClientSocket(e); 211 } 212 } 213 214 private void CloseClientSocket(SocketAsyncEventArgs e) 215 { 216 AsyncUserToken token = e.UserToken as AsyncUserToken; 217 218 // close the socket associated with the client 219 try 220 { 221 token.Socket.Shutdown(SocketShutdown.Send); 222 } 223 // throws if client process has already closed 224 catch (Exception) { } 225 token.Socket.Close(); 226 227 // decrement the counter keeping track of the total number of clients connected to the server 228 Interlocked.Decrement(ref m_numConnectedSockets); 229 230 // Free the SocketAsyncEventArg so they can be reused by another client 231 m_readWritePool.Push(e); 232 233 m_maxNumberAcceptedClients.Release(); 234 Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets); 235 } 236 }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Server server = new Server(100, 1024); 6 server.Init(); 7 server.Start(new IPEndPoint(IPAddress.Any, 8080)); 8 } 9 }