You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

172 lines
5.5 KiB

4 years ago
4 years ago
  1. using Apewer.Internals;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Threading;
  7. namespace Apewer.Network
  8. {
  9. /// <summary>TCP 服务端。</summary>
  10. public sealed class TcpServer : IDisposable
  11. {
  12. Socket _server = null;
  13. List<Socket> _clients = new List<Socket>(1024);
  14. /// <summary>获取当前监听的端口。</summary>
  15. public int Port { get => GetLocalPort(); }
  16. /// <summary>启动服务端,并在所有接口上监听端口。</summary>
  17. public TcpServer(int port) : this(IPAddress.Any, port) { }
  18. /// <summary>启动服务端,并监听端口。</summary>
  19. public TcpServer(string ip, int port) : this(IPAddress.Parse(ip), port) { }
  20. /// <summary>启动服务端,并监听端口。</summary>
  21. public TcpServer(IPAddress address, int port) : this(new IPEndPoint(address, port)) { }
  22. /// <summary>启动服务端,并监听端口。</summary>
  23. public TcpServer(IPEndPoint endpoint)
  24. {
  25. _server = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  26. _server.Bind(endpoint);
  27. _server.Listen(500);
  28. _server.SendTimeout = 5000;
  29. }
  30. /// <summary>关闭连接,释放系统资源。</summary>
  31. public void Dispose()
  32. {
  33. lock (_clients)
  34. {
  35. foreach (var client in _clients)
  36. {
  37. try { client.Disconnect(false); } catch { }
  38. try { client.Close(100); } catch { }
  39. #if !NET20
  40. try { client.Dispose(); } catch { }
  41. #endif
  42. }
  43. _clients.Clear();
  44. }
  45. if (_server != null)
  46. {
  47. try { _server.Disconnect(false); } catch { }
  48. try { _server.Close(100); } catch { }
  49. #if !NET20
  50. try { _server.Dispose(); } catch { }
  51. #endif
  52. }
  53. }
  54. /// <summary>获取当前所有客户端连接。</summary>
  55. public Socket[] GetClients()
  56. {
  57. lock (_clients)
  58. {
  59. var array = _clients.ToArray();
  60. return array;
  61. }
  62. }
  63. /// <summary>等待接收客户端连接。此方法将会阻塞当前线程直到关闭监听的端口。</summary>
  64. /// <param name="callback">处理客户端连接,当此回调结束时将释放此连接。</param>
  65. /// <exception cref="ArgumentNullException"></exception>
  66. public void Accept(Action<Socket> callback)
  67. {
  68. if (callback == null) throw new ArgumentNullException(nameof(callback));
  69. while (true)
  70. {
  71. var socket = null as Socket;
  72. try
  73. {
  74. socket = _server.Accept();
  75. if (socket != null)
  76. {
  77. Callback.Enqueue(this, socket, callback);
  78. }
  79. }
  80. catch
  81. {
  82. if (socket != null)
  83. {
  84. try { socket.Shutdown(SocketShutdown.Both); } catch { }
  85. try { socket.Close(); } catch { }
  86. try { socket.Disconnect(false); } catch { }
  87. #if !NET20
  88. try { socket.Dispose(); } catch { }
  89. #endif
  90. }
  91. }
  92. }
  93. }
  94. int GetLocalPort()
  95. {
  96. var socket = _server;
  97. if (socket != null)
  98. {
  99. var endpoint = socket.LocalEndPoint as IPEndPoint;
  100. if (endpoint != null) return endpoint.Port;
  101. }
  102. return 0;
  103. }
  104. sealed class Callback
  105. {
  106. TcpServer _server;
  107. Socket _socket;
  108. Action<Socket> _action;
  109. public static void Enqueue(TcpServer server, Socket socket, Action<Socket> callback)
  110. {
  111. var state = new Callback();
  112. state._server = server ?? throw new ArgumentNullException(nameof(server));
  113. state._socket = socket ?? throw new ArgumentNullException(nameof(socket));
  114. state._action = callback ?? throw new ArgumentNullException(nameof(callback));
  115. ThreadPool.QueueUserWorkItem(InThreadPool, state);
  116. }
  117. static void InThreadPool(object state)
  118. {
  119. var instance = state as Callback;
  120. if (instance == null) return;
  121. if (instance._server == null) return;
  122. if (instance._socket == null) return;
  123. if (instance._action == null) return;
  124. var socket = instance._socket;
  125. lock (instance._server._clients)
  126. {
  127. instance._server._clients.Add(instance._socket);
  128. }
  129. try
  130. {
  131. instance._action.Invoke(instance._socket);
  132. }
  133. catch
  134. { }
  135. finally
  136. {
  137. lock (instance._server._clients)
  138. {
  139. instance._server._clients.Remove(instance._socket);
  140. }
  141. try { socket.Disconnect(false); } catch { }
  142. try { socket.Close(100); } catch { }
  143. #if !NET20
  144. try { socket.Dispose(); } catch { }
  145. #endif
  146. }
  147. }
  148. }
  149. }
  150. }