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.

171 lines
5.4 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.Close(); } catch { }
  85. try { socket.Disconnect(false); } catch { }
  86. #if !NET20
  87. try { socket.Dispose(); } catch { }
  88. #endif
  89. }
  90. }
  91. }
  92. }
  93. int GetLocalPort()
  94. {
  95. var socket = _server;
  96. if (socket != null)
  97. {
  98. var endpoint = socket.LocalEndPoint as IPEndPoint;
  99. if (endpoint != null) return endpoint.Port;
  100. }
  101. return 0;
  102. }
  103. sealed class Callback
  104. {
  105. TcpServer _server;
  106. Socket _socket;
  107. Action<Socket> _action;
  108. public static void Enqueue(TcpServer server, Socket socket, Action<Socket> callback)
  109. {
  110. var state = new Callback();
  111. state._server = server ?? throw new ArgumentNullException(nameof(server));
  112. state._socket = socket ?? throw new ArgumentNullException(nameof(socket));
  113. state._action = callback ?? throw new ArgumentNullException(nameof(callback));
  114. ThreadPool.QueueUserWorkItem(InThreadPool, state);
  115. }
  116. static void InThreadPool(object state)
  117. {
  118. var instance = state as Callback;
  119. if (instance == null) return;
  120. if (instance._server == null) return;
  121. if (instance._socket == null) return;
  122. if (instance._action == null) return;
  123. var socket = instance._socket;
  124. lock (instance._server._clients)
  125. {
  126. instance._server._clients.Add(instance._socket);
  127. }
  128. try
  129. {
  130. instance._action.Invoke(instance._socket);
  131. }
  132. catch
  133. { }
  134. finally
  135. {
  136. lock (instance._server._clients)
  137. {
  138. instance._server._clients.Remove(instance._socket);
  139. }
  140. try { socket.Disconnect(false); } catch { }
  141. try { socket.Close(100); } catch { }
  142. #if !NET20
  143. try { socket.Dispose(); } catch { }
  144. #endif
  145. }
  146. }
  147. }
  148. }
  149. }