C#异步TCP服务器完整实现
C#异步TCP服务器完整实现
TCP异步Socket模型
C#的TCP异步Socket模型是通过Begin-End模式实现的。例如提供 BeginConnect 、 BeginAccept、 BeginSend 和 BeginReceive等。
IAsyncResult BeginAccept(AsyncCallback callback, object state);
Async Callback 回调在函数执行完毕后执行。state对象被用于在执行函数和回调函数间传输信息。
Socket socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint iep = new IPEndPoint(IPAddress.Any, 8888 ); socket.Bind(iep); socket.Listen( 5 ); socket.BeginAccept ( new AsyncCallback(CallbackAccept), socket); private void CallbackAccept(IAsyncResult iar) { Socket server = (Socket)iar.AsyncState; Socket client = server.EndAccept(iar); }
则在Accept一个TcpClient,需要维护TcpClient列表。
private List<TcpClientState> clients;
异步TCP服务器完整实现
1 /// <summary> 2 /// 异步TCP服务器 3 /// </summary> 4 public class AsyncTcpServer : IDisposable 5 { 6 #region Fields 7 8 private TcpListener listener; 9 private List<TcpClientState> clients; 10 private bool disposed = false ; 11 12 #endregion 13 14 #region Ctors 15 16 /// <summary> 17 /// 异步TCP服务器 18 /// </summary> 19 /// <param name="listenPort"> 监听的端口 </param> 20 public AsyncTcpServer( int listenPort) 21 : this (IPAddress.Any, listenPort) 22 { 23 } 24 25 /// <summary> 26 /// 异步TCP服务器 27 /// </summary> 28 /// <param name="localEP"> 监听的终结点 </param> 29 public AsyncTcpServer(IPEndPoint localEP) 30 : this (localEP.Address, localEP.Port) 31 { 32 } 33 34 /// <summary> 35 /// 异步TCP服务器 36 /// </summary> 37 /// <param name="localIPAddress"> 监听的IP地址 </param> 38 /// <param name="listenPort"> 监听的端口 </param> 39 public AsyncTcpServer(IPAddress localIPAddress, int listenPort) 40 { 41 Address = localIPAddress; 42 Port = listenPort; 43 this .Encoding = Encoding.Default; 44 45 clients = new List<TcpClientState> (); 46 47 listener = new TcpListener(Address, Port); 48 listener.AllowNatTraversal( true ); 49 } 50 51 #endregion 52 53 #region Properties 54 55 /// <summary> 56 /// 服务器是否正在运行 57 /// </summary> 58 public bool IsRunning { get ; private set ; } 59 /// <summary> 60 /// 监听的IP地址 61 /// </summary> 62 public IPAddress Address { get ; private set ; } 63 /// <summary> 64 /// 监听的端口 65 /// </summary> 66 public int Port { get ; private set ; } 67 /// <summary> 68 /// 通信使用的编码 69 /// </summary> 70 public Encoding Encoding { get ; set ; } 71 72 #endregion 73 74 #region Server 75 76 /// <summary> 77 /// 启动服务器 78 /// </summary> 79 /// <returns> 异步TCP服务器 </returns> 80 public AsyncTcpServer Start() 81 { 82 if (! IsRunning) 83 { 84 IsRunning = true ; 85 listener.Start(); 86 listener.BeginAcceptTcpClient( 87 new AsyncCallback(HandleTcpClientAccepted), listener); 88 } 89 return this ; 90 } 91 92 /// <summary> 93 /// 启动服务器 94 /// </summary> 95 /// <param name="backlog"> 96 /// 服务器所允许的挂起连接序列的最大长度 97 /// </param> 98 /// <returns> 异步TCP服务器 </returns> 99 public AsyncTcpServer Start( int backlog) 100 { 101 if (! IsRunning) 102 { 103 IsRunning = true ; 104 listener.Start(backlog); 105 listener.BeginAcceptTcpClient( 106 new AsyncCallback(HandleTcpClientAccepted), listener); 107 } 108 return this ; 109 } 110 111 /// <summary> 112 /// 停止服务器 113 /// </summary> 114 /// <returns> 异步TCP服务器 </returns> 115 public AsyncTcpServer Stop() 116 { 117 if (IsRunning) 118 { 119 IsRunning = false ; 120 listener.Stop(); 121 122 lock ( this .clients) 123 { 124 for ( int i = 0 ; i < this .clients.Count; i++ ) 125 { 126 this .clients[i].TcpClient.Client.Disconnect( false ); 127 } 128 this .clients.Clear(); 129 } 130 131 } 132 return this ; 133 } 134 135 #endregion 136 137 #region Receive 138 139 private void HandleTcpClientAccepted(IAsyncResult ar) 140 { 141 if (IsRunning) 142 { 143 TcpListener tcpListener = (TcpListener)ar.AsyncState; 144 145 TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar); 146 byte [] buffer = new byte [tcpClient.ReceiveBufferSize]; 147 148 TcpClientState internalClient 149 = new TcpClientState(tcpClient, buffer); 150 lock ( this .clients) 151 { 152 this .clients.Add(internalClient); 153 RaiseClientConnected(tcpClient); 154 } 155 156 NetworkStream networkStream = internalClient.NetworkStream; 157 networkStream.BeginRead( 158 internalClient.Buffer, 159 0 , 160 internalClient.Buffer.Length, 161 HandleDatagramReceived, 162 internalClient); 163 164 tcpListener.BeginAcceptTcpClient( 165 new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState); 166 } 167 } 168 169 private void HandleDatagramReceived(IAsyncResult ar) 170 { 171 if (IsRunning) 172 { 173 TcpClientState internalClient = (TcpClientState)ar.AsyncState; 174 NetworkStream networkStream = internalClient.NetworkStream; 175 176 int numberOfReadBytes = 0 ; 177 try 178 { 179 numberOfReadBytes = networkStream.EndRead(ar); 180 } 181 catch 182 { 183 numberOfReadBytes = 0 ; 184 } 185 186 if (numberOfReadBytes == 0 ) 187 { 188 // connection has been closed 189 lock ( this .clients) 190 { 191 this .clients.Remove(internalClient); 192 RaiseClientDisconnected(internalClient.TcpClient); 193 return ; 194 } 195 } 196 197 // received byte and trigger event notification 198 byte [] receivedBytes = new byte [numberOfReadBytes]; 199 Buffer.BlockCopy( 200 internalClient.Buffer, 0 , 201 receivedBytes, 0 , numberOfReadBytes); 202 RaiseDatagramReceived(internalClient.TcpClient, receivedBytes); 203 RaisePlaintextReceived(internalClient.TcpClient, receivedBytes); 204 205 // continue listening for tcp datagram packets 206 networkStream.BeginRead( 207 internalClient.Buffer, 208 0 , 209 internalClient.Buffer.Length, 210 HandleDatagramReceived, 211 internalClient); 212 } 213 } 214 215 #endregion 216 217 #region Events 218 219 /// <summary> 220 /// 接收到数据报文事件 221 /// </summary> 222 public event EventHandler<TcpDatagramReceivedEventArgs< byte []>> DatagramReceived; 223 /// <summary> 224 /// 接收到数据报文明文事件 225 /// </summary> 226 public event EventHandler<TcpDatagramReceivedEventArgs< string >> PlaintextReceived; 227 228 private void RaiseDatagramReceived(TcpClient sender, byte [] datagram) 229 { 230 if (DatagramReceived != null ) 231 { 232 DatagramReceived( this , new TcpDatagramReceivedEventArgs< byte []> (sender, datagram)); 233 } 234 } 235 236 private void RaisePlaintextReceived(TcpClient sender, byte [] datagram) 237 { 238 if (PlaintextReceived != null ) 239 { 240 PlaintextReceived( this , new TcpDatagramReceivedEventArgs< string > ( 241 sender, this .Encoding.GetString(datagram, 0 , datagram.Length))); 242 } 243 } 244 245 /// <summary> 246 /// 与客户端的连接已建立事件 247 /// </summary> 248 public event EventHandler<TcpClientConnectedEventArgs> ClientConnected; 249 /// <summary> 250 /// 与客户端的连接已断开事件 251 /// </summary> 252 public event EventHandler<TcpClientDisconnectedEventArgs> ClientDisconnected; 253 254 private void RaiseClientConnected(TcpClient tcpClient) 255 { 256 if (ClientConnected != null ) 257 { 258 ClientConnected( this , new TcpClientConnectedEventArgs(tcpClient)); 259 } 260 } 261 262 private void RaiseClientDisconnected(TcpClient tcpClient) 263 { 264 if (ClientDisconnected != null ) 265 { 266 ClientDisconnected( this , new TcpClientDisconnectedEventArgs(tcpClient)); 267 } 268 } 269 270 #endregion 271 272 #region Send 273 274 /// <summary> 275 /// 发送报文至指定的客户端 276 /// </summary> 277 /// <param name="tcpClient"> 客户端 </param> 278 /// <param name="datagram"> 报文 </param> 279 public void Send(TcpClient tcpClient, byte [] datagram) 280 { 281 if (! IsRunning) 282 throw new InvalidProgramException( " This TCP server has not been started. " ); 283 284 if (tcpClient == null ) 285 throw new ArgumentNullException( " tcpClient " ); 286 287 if (datagram == null ) 288 throw new ArgumentNullException( " datagram " ); 289 290 tcpClient.GetStream().BeginWrite( 291 datagram, 0 , datagram.Length, HandleDatagramWritten, tcpClient); 292 } 293 294 private void HandleDatagramWritten(IAsyncResult ar) 295 { 296 ((TcpClient)ar.AsyncState).GetStream().EndWrite(ar); 297 } 298 299 /// <summary> 300 /// 发送报文至指定的客户端 301 /// </summary> 302 /// <param name="tcpClient"> 客户端 </param> 303 /// <param name="datagram"> 报文 </param> 304 public void Send(TcpClient tcpClient, string datagram) 305 { 306 Send(tcpClient, this .Encoding.GetBytes(datagram)); 307 } 308 309 /// <summary> 310 /// 发送报文至所有客户端 311 /// </summary> 312 /// <param name="datagram"> 报文 </param> 313 public void SendAll( byte [] datagram) 314 { 315 if (! IsRunning) 316 throw new InvalidProgramException( " This TCP server has not been started. " ); 317 318 for ( int i = 0 ; i < this .clients.Count; i++ ) 319 { 320 Send( this .clients[i].TcpClient, datagram); 321 } 322 } 323 324 /// <summary> 325 /// 发送报文至所有客户端 326 /// </summary> 327 /// <param name="datagram"> 报文 </param> 328 public void SendAll( string datagram) 329 { 330 if (! IsRunning) 331 throw new InvalidProgramException( " This TCP server has not been started. " ); 332 333 SendAll( this .Encoding.GetBytes(datagram)); 334 } 335 336 #endregion 337 338 #region IDisposable Members 339 340 /// <summary> 341 /// Performs application-defined tasks associated with freeing, 342 /// releasing, or resetting unmanaged resources. 343 /// </summary> 344 public void Dispose() 345 { 346 Dispose( true ); 347 GC.SuppressFinalize( this ); 348 } 349 350 /// <summary> 351 /// Releases unmanaged and - optionally - managed resources 352 /// </summary> 353 /// <param name="disposing"><c> true </c> to release 354 /// both managed and unmanaged resources; <c> false </c> 355 /// to release only unmanaged resources. </param> 356 protected virtual void Dispose( bool disposing) 357 { 358 if (! this .disposed) 359 { 360 if (disposing) 361 { 362 try 363 { 364 Stop(); 365 366 if (listener != null ) 367 { 368 listener = null ; 369 } 370 } 371 catch (SocketException ex) 372 { 373 ExceptionHandler.Handle(ex); 374 } 375 } 376 377 disposed = true ; 378 } 379 } 380 381 #endregion 382 }
使用举例
1 class Program 2 { 3 static AsyncTcpServer server; 4 5 static void Main( string [] args) 6 { 7 LogFactory.Assign( new ConsoleLogFactory()); 8 9 server = new AsyncTcpServer( 9999 ); 10 server.Encoding = Encoding.UTF8; 11 server.ClientConnected += 12 new EventHandler<TcpClientConnectedEventArgs> (server_ClientConnected); 13 server.ClientDisconnected += 14 new EventHandler<TcpClientDisconnectedEventArgs> (server_ClientDisconnected); 15 server.PlaintextReceived += 16 new EventHandler<TcpDatagramReceivedEventArgs< string >> (server_PlaintextReceived); 17 server.Start(); 18 19 Console.WriteLine( " TCP server has been started. " ); 20 Console.WriteLine( " Type something to send to client... " ); 21 while ( true ) 22 { 23 string text = Console.ReadLine(); 24 server.SendAll(text); 25 } 26 } 27 28 static void server_ClientConnected( object sender, TcpClientConnectedEventArgs e) 29 { 30 Logger.Debug( string .Format(CultureInfo.InvariantCulture, 31 " TCP client {0} has connected. " , 32 e.TcpClient.Client.RemoteEndPoint.ToString())); 33 } 34 35 static void server_ClientDisconnected( object sender, TcpClientDisconnectedEventArgs e) 36 { 37 Logger.Debug( string .Format(CultureInfo.InvariantCulture, 38 " TCP client {0} has disconnected. " , 39 e.TcpClient.Client.RemoteEndPoint.ToString())); 40 } 41 42 static void server_PlaintextReceived( object sender, TcpDatagramReceivedEventArgs< string > e) 43 { 44 if (e.Datagram != " Received " ) 45 { 46 Console.Write( string .Format( " Client : {0} --> " , 47 e.TcpClient.Client.RemoteEndPoint.ToString())); 48 Console.WriteLine( string .Format( " {0} " , e.Datagram)); 49 server.Send(e.TcpClient, " Server has received you text : " + e.Datagram); 50 } 51 } 52 }
标签: C# , .NET , TCP
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did46168