TcpServer


 

  1     /// <summary>
  2     /// 從 TCP 網絡客戶端偵聽連接。
  3     /// </summary>
  4     public class TcpServer
  5     {
  6         private bool isClosing = false; // 服務器 closing 標志位
  7         private TcpListener server; // 服務器的 TcpListener
  8         private Action<TcpConnection> connectinAction; // 每個客戶端連接的委托
  9 
 10         /// <summary>
 11         /// 服務器運行狀態
 12         /// </summary>
 13         public bool IsRunning { get; private set; }
 14 
 15         /// <summary>
 16         /// 使用指定的本地終結點初始化 TcpServer 類的新實例。
 17         /// </summary>
 18         /// <param name="localEP">將 TcpServer 綁定到的本地終結點。</param>
 19         public TcpServer(IPEndPoint localEP)
 20         {
 21             IsRunning = false;
 22             server = new TcpListener(localEP);
 23         }
 24 
 25         /// <summary>
 26         /// 初始化 TcpServer 類的新實例,該類在指定的本地 IP 地址和端口號上偵聽是否有傳入的連接嘗試。
 27         /// </summary>
 28         /// <param name="localaddr">本地 IP 地址</param>
 29         /// <param name="port">用來偵聽傳入的連接嘗試的端口。</param>
 30         public TcpServer(IPAddress localaddr, int port) : this(new IPEndPoint(localaddr, port))
 31         {
 32         }
 33 
 34         /// <summary>
 35         /// 初始化 TcpServer 類的新實例,該類在指定的本地 IP 地址和端口號上偵聽是否有傳入的連接嘗試。
 36         /// </summary>
 37         /// <param name="address">本地 IP 地址字符串</param>
 38         /// <param name="port">用來偵聽傳入的連接嘗試的端口。</param>
 39         public TcpServer(string address, int port) : this(IPAddress.Parse(address), port)
 40         {
 41         }
 42 
 43         /// <summary>
 44         /// 開始偵聽傳入的連接請求。
 45         /// </summary>
 46         /// <param name="action">每個客戶端連接對應的委托</param>
 47         public void Start(Action<TcpConnection> action)
 48         {
 49             if (!IsRunning)
 50             {
 51                 IsRunning = true;
 52                 isClosing = false;
 53                 connectinAction = action;
 54                 server.Start();
 55                 StartAccept();
 56             }
 57         }
 58 
 59         /// <summary>
 60         /// 獲取當前實例的基礎 EndPoint。
 61         /// </summary>
 62         public EndPoint LocalEndPoint
 63         {
 64             get { return server.LocalEndpoint; }
 65         }
 66 
 67         /// <summary>
 68         /// 獲取或設置一個 bool 值,該值指定該實例是否只允許一個基礎套接字來偵聽特定端口。
 69         /// </summary>
 70         public bool ExclusiveAddressUse
 71         {
 72             get { return server.ExclusiveAddressUse; }
 73             set { server.ExclusiveAddressUse = value; }
 74         }
 75 
 76         /// <summary>
 77         /// 獲取基礎網絡 Socket
 78         /// </summary>
 79         public Socket Server
 80         {
 81             get { return server.Server; }
 82         }
 83 
 84         /// <summary>
 85         /// 關閉偵聽器。
 86         /// </summary>
 87         public void Stop()
 88         {
 89             isClosing = true;
 90             server.Stop();
 91             IsRunning = false;
 92         }
 93 
 94         /// <summary>
 95         /// 開始一個異步操作來接受一個傳入的連接嘗試。
 96         /// </summary>
 97         private void StartAccept()
 98         {
 99             server.BeginAcceptTcpClient(iar =>
100             {
101                 try
102                 {
103                     if (isClosing) return;
104                     TcpClient tcpClient = server.EndAcceptTcpClient(iar);
105                     TcpConnection client = new TcpConnection(tcpClient, connectinAction);
106                 }
107                 catch (Exception ex)
108                 {
109                     Trace.TraceError(ex.Message);
110                 }
111                 StartAccept();
112             }, null);
113 
114         }
115 
116         /// <summary>
117         /// 返回指定終結點的 IP 地址和端口號。
118         /// </summary>
119         /// <returns>包含指定終結點(例如,192.168.1.2:80)的 IP 地址和端口號的字符串。</returns>
120         public override string ToString()
121         {
122             return LocalEndPoint.ToString();
123         }
124 
125         /// <summary>
126         /// 獲取本地IP地址
127         /// </summary>
128         /// <param name="addressFamily">地址族,默認IPv4</param>
129         /// <returns>本地IP地址數組</returns>
130         public static IPAddress[] GetLocalAddress(AddressFamily addressFamily = AddressFamily.InterNetwork)
131         {
132             List<IPAddress> list = new List<IPAddress>();
133             list.Add(IPAddress.Any);
134             list.Add(IPAddress.Loopback);
135             foreach (var item in Dns.GetHostAddresses(Dns.GetHostName()))
136             {
137                 if (item.AddressFamily == addressFamily)
138                     list.Add(item);
139             }
140             return list.ToArray();
141         }
142 
143         /// <summary>
144         /// 獲取正在使用中的端口
145         /// </summary>
146         /// <param name="address">指定IP地址,默認全部地址</param>
147         /// <returns>正在使用中的端口數組</returns>
148         public static int[] GetInUsedPort(string address = null)
149         {
150             List<IPEndPoint> localEP = new List<IPEndPoint>();
151             List<int> localPort = new List<int>();
152             IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
153             localEP.AddRange(ipGlobalProperties.GetActiveTcpListeners());
154             localEP.AddRange(ipGlobalProperties.GetActiveUdpListeners());
155             localEP.AddRange(ipGlobalProperties.GetActiveTcpConnections().Select(item => item.LocalEndPoint));
156             foreach (var item in localEP.Distinct())
157             {
158                 if (address == null || item.Address.ToString() == address)
159                     localPort.Add(item.Port);
160             }
161             localPort.Sort();
162             return localPort.Distinct().ToArray();
163         }
164 
165         /// <summary>
166         /// 隨機獲取一個大於等於 min 的空閑端口
167         /// </summary>
168         /// <param name="min">指定最小空閑端口,默認1024</param>
169         /// <param name="address">指定IP地址,默認全部地址</param>
170         /// <returns>一個指定IP地址與范圍的隨機空閑端口</returns>
171         public static int GetFreePort(int min = 1024, string address = null)
172         {
173             int freePort = -1;
174             Random random = new Random();
175             int[] freePorts = GetInUsedPort(address)
176                 .Where(x => x >= (min = min <= 0 ? 1 : min))
177                 .ToArray();
178             while (freePort < 0)
179             {
180                 freePort = random.Next(min, 65536);
181                 foreach (var item in freePorts)
182                 {
183                     if (freePort == item)
184                         freePort = -1;
185                 }
186             }
187             return freePort;
188         }
189     }
190 
191     /// <summary>
192     /// TCP 網絡服務的一個客戶端連接。
193     /// </summary>
194     public class TcpConnection : IDisposable
195     {
196         private bool isClosing = false; // 當前連接 closing 標志位
197         private byte[] receiveBuffer; // 當前連接用於 receive 的數據緩沖區
198         private TcpClient tcpClient; // 當前連接的 TcpClient 對象
199         private NetworkStream networkStream; // 當前連接的 NetworkerStream 對象
200         private readonly Action<TcpConnection> initialize; // 當前連接的委托
201 
202         /// <summary>
203         /// 通過指定的 TcpClient 和 Action 實例化一個與服務器建立連接的客戶端。
204         /// </summary>
205         /// <param name="client">指定的 TcpClient</param>
206         /// <param name="action">客戶端委托</param>
207         public TcpConnection(TcpClient client, Action<TcpConnection> action)
208         {
209             tcpClient = client;
210             initialize = action;
211             networkStream = tcpClient.GetStream();
212             RemoteEndPoint = CopyEndPoint(tcpClient.Client.RemoteEndPoint);
213             OnAccept = x => { };
214             OnReceive = (x, y) => { };
215             OnClose = (x, y) => { };
216             OnError = (x, y) => { };
217             initialize(this);
218             OnAccept(this);
219             receiveBuffer = new byte[tcpClient.ReceiveBufferSize];
220             StartReceive();
221         }
222 
223         /// <summary>
224         /// 獲取 EndPoint 的深拷貝
225         /// </summary>
226         /// <param name="endPoint">需要拷貝的源對象</param>
227         /// <returns>目標副本</returns>
228         private EndPoint CopyEndPoint(EndPoint endPoint)
229         {
230             return endPoint.Create(endPoint.Serialize());
231         }
232 
233         /// <summary>
234         /// 從當前連接開始異步讀取。
235         /// </summary>
236         private void StartReceive()
237         {
238             try
239             {
240                 networkStream.BeginRead(receiveBuffer, 0, receiveBuffer.Length, iar =>
241                 {
242                     try
243                     {
244                         if (isClosing) return;
245                         int size = networkStream.EndRead(iar);
246                         if (size == 0 || !tcpClient.Connected || !networkStream.CanRead)
247                         {
248                             Close(true);
249                             return;
250                         }
251                         byte[] data = new byte[size];
252                         Buffer.BlockCopy(receiveBuffer, 0, data, 0, size);
253                         OnReceive(this, data);
254                     }
255                     catch (Exception ex)
256                     {
257                         OnError(this, ex);
258                     }
259                     StartReceive();
260                 }, null);
261             }
262             catch (Exception ex)
263             {
264                 OnError(this, ex);
265             }
266         }
267 
268         /// <summary>
269         /// 獲取或設置一個值,該值在發送或接收緩沖區未滿時禁用延遲。
270         /// </summary>
271         public bool NoDelay
272         {
273             get { return tcpClient.NoDelay; }
274             set { tcpClient.NoDelay = value; }
275         }
276 
277         /// <summary>
278         /// 獲取已經從網絡接收且可供讀取的數據量。
279         /// </summary>
280         public int Available
281         {
282             get { return tcpClient.Available; }
283         }
284 
285         /// <summary>
286         /// 獲取或設置基礎 Socket。
287         /// </summary>
288         public Socket Client
289         {
290             get { return tcpClient.Client; }
291         }
292 
293         /// <summary>
294         /// 獲取當前連接的終節點
295         /// </summary>
296         public EndPoint RemoteEndPoint
297         {
298             get; private set;
299         }
300 
301         /// <summary>
302         /// 獲取一個 bool 值,該值指示 Socket 是否已連接到遠程主機。
303         /// </summary>
304         public bool Connected
305         {
306             get { return tcpClient.Connected; }
307         }
308 
309         /// <summary>
310         /// 獲取或設置 bool 值,該值指定是否只允許一個客戶端使用端口。
311         /// </summary>
312         public bool ExclusiveAddressUse
313         {
314             get { return tcpClient.ExclusiveAddressUse; }
315             set { tcpClient.ExclusiveAddressUse = value; }
316         }
317 
318         /// <summary>
319         /// 開始向當前連接異步寫入。
320         /// </summary>
321         /// <param name="data">類型 Byte 的數組,該數組包含要寫入的數據。</param>
322         /// <returns></returns>
323         public IAsyncResult Send(byte[] data)
324         {
325             try
326             {
327                 return networkStream.BeginWrite(data, 0, data.Length, iar =>
328                 {
329                     try
330                     {
331                         if (!tcpClient.Connected || !networkStream.CanRead)
332                         {
333                             Close(true);
334                             return;
335                         }
336                         networkStream.EndWrite(iar);
337                     }
338                     catch (Exception ex)
339                     {
340                         OnError(this, ex);
341                     }
342                 }, null);
343             }
344             catch (Exception ex)
345             {
346                 OnError(this, ex);
347             }
348             return null;
349         }
350 
351         /// <summary>
352         /// 開始向當前連接異步寫入。
353         /// </summary>
354         /// <param name="message">該 string 包含要寫入的數據。</param>
355         /// <returns></returns>
356         public IAsyncResult Send(string message)
357         {
358             return Send(Encoding.UTF8.GetBytes(message));
359         }
360 
361         /// <summary>
362         /// 關閉基礎連接並釋放所有資源
363         /// </summary>
364         /// <param name="activeExit">是否主動關閉</param>
365         private void Close(bool activeExit)
366         {
367             isClosing = true;
368             networkStream.Close();
369             tcpClient.Close();
370             OnClose(this, activeExit);
371         }
372 
373         /// <summary>
374         /// 關閉基礎連接並釋放所有資源
375         /// </summary>
376         public void Close()
377         {
378             Close(false);
379         }
380 
381         /// <summary>
382         /// 關閉基礎連接並釋放所有資源,和Close()效果相同
383         /// </summary>
384         public void Dispose()
385         {
386             Close(false);
387         }
388 
389         /// <summary>
390         /// 返回指定終結點的 IP 地址和端口號。
391         /// </summary>
392         /// <returns>包含指定終結點(例如,192.168.1.2:80)的 IP 地址和端口號的字符串。</returns>
393         public override string ToString()
394         {
395             return RemoteEndPoint.ToString();
396         }
397 
398         /// <summary>
399         /// TcpConnection 的 accept 事件
400         /// </summary>
401         public Action<TcpConnection> OnAccept { get; set; }
402 
403         /// <summary>
404         /// TcpConnection 的 receive 事件
405         /// </summary>
406         public Action<TcpConnection, byte[]> OnReceive { get; set; }
407 
408         /// <summary>
409         /// TcpConnection 的 close 事件
410         /// </summary>
411         public Action<TcpConnection, bool> OnClose { get; set; }
412 
413         /// <summary>
414         /// TcpConnection 的 error 事件
415         /// </summary>
416         public Action<TcpConnection, Exception> OnError { get; set; }
417 
418     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM