C#的TCP异步Socket模型是通过Begin-End模式实现的。例如提供 BeginConnect、BeginAccept、BeginSend 和 BeginReceive等。
IAsyncResult BeginAccept(AsyncCallback callback, object state);
AsyncCallback 回调在函数执行完毕后执行。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 #endregion136 137 #region Receive138 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 try178 {179 numberOfReadBytes = networkStream.EndRead(ar);180 }181 catch182 {183 numberOfReadBytes = 0;184 }185 186 if (numberOfReadBytes == 0)187 {188 // connection has been closed189 lock (this.clients)190 {191 this.clients.Remove(internalClient);192 RaiseClientDisconnected(internalClient.TcpClient);193 return;194 }195 }196 197 // received byte and trigger event notification198 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 packets206 networkStream.BeginRead(207 internalClient.Buffer, 208 0, 209 internalClient.Buffer.Length, 210 HandleDatagramReceived, 211 internalClient);212 }213 }214 215 #endregion216 217 #region Events218 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 #endregion271 272 #region Send273 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 #endregion337 338 #region IDisposable Members339 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 resources352 /// </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 try363 {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 #endregion382 }
使用举例
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 }
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦