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.

216 lines
6.5 KiB

3 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net;
  4. using System.Net.Sockets;
  5. using System.Security.Authentication;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Text;
  8. using System.Threading;
  9. namespace Apewer.Web
  10. {
  11. /// <summary></summary>
  12. public sealed class MiniServer
  13. {
  14. /// <summary></summary>
  15. public MiniServer()
  16. {
  17. SynchronousIO = true;
  18. MaxRequest = 1024 * 1024 * 1024;
  19. }
  20. #region configuration
  21. /// <summary>上下文处理程序。</summary>
  22. /// <remarks>默认值:未指定</remarks>
  23. public Action<MiniContext> Handler { get; set; }
  24. /// <summary>捕获处理请求的异常。</summary>
  25. /// <remarks>默认值:未指定</remarks>
  26. public Action<Exception> CatchException { get; set; }
  27. /// <summary>获取已指定的本地终结点。</summary>
  28. public IPEndPoint IPEndPoint { get; private set; }
  29. /// <summary>获取已监听的本地端口。</summary>
  30. public int Port { get => GetPort(); }
  31. /// <summary>入站超时毫秒数。指定 0 或 -1 时为无限大。</summary>
  32. /// <remarks>默认值:0</remarks>
  33. public int InboundTimeout { get; set; }
  34. /// <summary>出站超时毫秒数。指定 0 或 -1 时为无限大。</summary>
  35. /// <remarks>默认值:0</remarks>
  36. public int OutboundTimeout { get; set; }
  37. /// <summary>SSL 证书。</summary>
  38. internal X509Certificate SslCertificate { get; set; }
  39. /// <summary>SSL 域名。</summary>
  40. internal string SslDomain { get; set; }
  41. /// <summary>SSL 协议。</summary>
  42. internal SslProtocols SslProtocols { get; set; }
  43. /// <summary>同步 IO 操作。</summary>
  44. /// <remarks>默认值:True</remarks>
  45. internal bool SynchronousIO { get; set; }
  46. /// <summary>对响应体分块。</summary>
  47. /// <remarks>默认值:False</remarks>
  48. public bool Chunked { get; set; }
  49. /// <summary>限制请求大小。</summary>
  50. /// <remarks>默认值:1 GB。</remarks>
  51. public int MaxRequest { get; set; }
  52. /// <summary>允许压缩。</summary>
  53. public bool Compression { get; set; }
  54. #endregion
  55. #region launcher
  56. Thread _thread = null;
  57. /// <summary>启动服务器。</summary>
  58. /// <exception cref="ArgumentNullException"></exception>
  59. /// <exception cref="ArgumentOutOfRangeException"></exception>
  60. /// <exception cref="InvalidOperationException"></exception>
  61. /// <exception cref="SocketException"></exception>
  62. public void Run(int port = 0, bool await = true)
  63. {
  64. if (port < 0 || port > 65535) throw new ArgumentOutOfRangeException(nameof(port));
  65. Run(new IPEndPoint(IPAddress.Any, port), await);
  66. }
  67. /// <summary>启动服务器。</summary>
  68. /// <exception cref="ArgumentNullException"></exception>
  69. /// <exception cref="InvalidOperationException"></exception>
  70. /// <exception cref="SocketException"></exception>
  71. public void Run(IPEndPoint ipEndpoint, bool await = true)
  72. {
  73. if (_socket != null) throw new InvalidOperationException("存在已启动的 Socket。");
  74. if (ipEndpoint == null) throw new ArgumentNullException(nameof(ipEndpoint));
  75. IPEndPoint = ipEndpoint;
  76. var ipep = ipEndpoint;
  77. _socket = new Socket(ipep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  78. _socket.Bind(ipep);
  79. _socket.Listen(500);
  80. var inTimeout = InboundTimeout;
  81. var outTimeout = OutboundTimeout;
  82. if (inTimeout > 0) _socket.ReceiveTimeout = inTimeout;
  83. if (outTimeout > 0) _socket.SendTimeout = outTimeout;
  84. _thread = new Thread(Listen);
  85. _thread.IsBackground = true;
  86. _thread.Start();
  87. if (await) while (_socket != null) Thread.Sleep(300);
  88. }
  89. /// <summary>关闭服务器。</summary>
  90. public void Shutdown()
  91. {
  92. var socket = _socket;
  93. if (socket != null)
  94. {
  95. try { socket.Shutdown(SocketShutdown.Both); } catch { }
  96. try { socket.Close(1); } catch { }
  97. _socket = null;
  98. }
  99. }
  100. #endregion
  101. #region listener
  102. Socket _socket = null;
  103. int GetPort()
  104. {
  105. var socket = _socket;
  106. if (socket != null) return 0;
  107. try { if (socket.LocalEndPoint is IPEndPoint ipep) return ipep.Port; } catch { }
  108. return 0;
  109. }
  110. void Listen()
  111. {
  112. if (SynchronousIO)
  113. {
  114. while (_socket != null)
  115. {
  116. var socket = _socket.Accept();
  117. if (socket != null) ThreadPool.QueueUserWorkItem(Process, socket);
  118. }
  119. }
  120. else
  121. {
  122. var e = new SocketAsyncEventArgs();
  123. e.UserToken = this;
  124. e.Completed += (sender, arg) => Process(arg);
  125. Socket dummy = null;
  126. Accept(e, ref dummy);
  127. }
  128. }
  129. void Accept(SocketAsyncEventArgs e, ref Socket socket)
  130. {
  131. e.AcceptSocket = null;
  132. var async = false;
  133. try
  134. {
  135. async = _socket.AcceptAsync(e);
  136. }
  137. catch
  138. {
  139. if (socket != null)
  140. {
  141. try { socket.Close(); } catch { }
  142. socket = null;
  143. }
  144. return;
  145. }
  146. if (!async) Process(e);
  147. }
  148. void Process(SocketAsyncEventArgs e)
  149. {
  150. var socket = null as Socket;
  151. if (e.SocketError == SocketError.Success) socket = e.AcceptSocket;
  152. Accept(e, ref socket);
  153. if (socket == null) return;
  154. Process(socket);
  155. }
  156. void Process(object obj)
  157. {
  158. var socket = obj as Socket;
  159. if (socket == null) return;
  160. try
  161. {
  162. var conn = null as MiniConnection;
  163. conn = new MiniConnection(this, socket);
  164. conn.BeginRead();
  165. }
  166. catch (Exception ex)
  167. {
  168. Shutdown();
  169. Logger.Web.Exception(this, ex);
  170. CatchException?.Invoke(ex);
  171. return;
  172. }
  173. }
  174. #endregion
  175. }
  176. }