C#异步TCP服务器完整实现

来源:互联网 发布:淘宝店logo尺寸大小 编辑:程序博客网 时间:2024/04/29 12:31
本文为 Dennis Gao 原创技术文章 转载地址http://www.cnblogs.com/gaochundong/archive/2013/04/14/csharp_async_tcp_server.html

TCP异步Socket模型

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   }
复制代码
0 0
原创粉丝点击