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.

462 lines
14 KiB

4 years ago
4 years ago
4 years ago
  1. #if !NET20
  2. using Apewer;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. namespace Apewer.WebSocket
  7. {
  8. /// <summary></summary>
  9. public sealed class GenericServer : IDisposable
  10. {
  11. private Dictionary<int, Connection> _connections = new Dictionary<int, Connection>();
  12. private WebSocketServer _server = null;
  13. private bool _running = false;
  14. private IPAddress _address = null;
  15. private int _port = 0;
  16. /// <summary></summary>
  17. public event SocketEvent OnOpen;
  18. /// <summary></summary>
  19. public event SocketEvent OnClose;
  20. /// <summary></summary>
  21. public event SocketEvent<string> OnMessage;
  22. /// <summary></summary>
  23. public event SocketEvent<byte[]> OnBytes;
  24. /// <summary></summary>
  25. public event SocketEvent<byte[]> OnPing;
  26. /// <summary></summary>
  27. public event SocketEvent<byte[]> OnPong;
  28. /// <summary></summary>
  29. public event SocketEvent<Exception> OnError;
  30. /// <summary></summary>
  31. public event ServerEvent<Exception> Excepted;
  32. #region Properties
  33. /// <summary>正在监听。</summary>
  34. public bool Running
  35. {
  36. get { return _running; }
  37. }
  38. /// <summary>监听的地址。</summary>
  39. public IPAddress Address
  40. {
  41. get { return _address; }
  42. }
  43. /// <summary>监听的端口号。</summary>
  44. public int Port
  45. {
  46. get { return _port; }
  47. }
  48. /// <summary>当前 Sockets 数量。</summary>
  49. public int Count
  50. {
  51. get
  52. {
  53. var count = 0;
  54. lock (_connections) { count = _connections.Count; }
  55. return count;
  56. }
  57. }
  58. #endregion
  59. #region Methods
  60. /// <summary></summary>
  61. public GenericServer() { }
  62. /// <summary>关闭服务,并释放系统资源。</summary>
  63. public void Dispose()
  64. {
  65. lock (_connections)
  66. {
  67. _connections.Clear();
  68. }
  69. lock (_server)
  70. {
  71. if (_server != null)
  72. {
  73. _server.Dispose();
  74. }
  75. }
  76. _server = null;
  77. _running = false;
  78. }
  79. /// <summary>启动监听,如果监听正在运行则失败。在所有 IPv4 网络接口上自动选择可用的端口号。</summary>
  80. /// <returns>已监听的端口号。</returns>
  81. /// <exception cref="FormatException" />
  82. /// <exception cref="InvalidOperationException" />
  83. public void Start() => Start(0, null);
  84. /// <summary>启动监听,如果监听正在运行则失败。</summary>
  85. /// <param name="endPoint">要监听的终结点。指定为 NULL 时将在所有 IPv4 网络接口上自动选择可用的端口号。</param>
  86. /// <returns>已监听的端口号。</returns>
  87. /// <exception cref="ArgumentOutOfRangeException" />
  88. /// <exception cref="FormatException" />
  89. /// <exception cref="InvalidOperationException" />
  90. public void Start(IPEndPoint endPoint)
  91. {
  92. if (endPoint == null) Start(0, null);
  93. else Start(endPoint.Port, endPoint.Address);
  94. }
  95. /// <summary>启动监听,如果监听正在运行则失败。</summary>
  96. /// <param name="port">要监听的端口号。指定为 0 时将自动选择可用的端口号。</param>
  97. /// <param name="address">要监听的网络接口。指定为 NULL 时将在所有 IPv4 网络接口监听,等同于 <see cref="IPAddress.Any"/>。</param>
  98. /// <returns>已监听的端口号。</returns>
  99. /// <exception cref="ArgumentOutOfRangeException" />
  100. /// <exception cref="FormatException" />
  101. /// <exception cref="InvalidOperationException" />
  102. public void Start(int port, IPAddress address = null)
  103. {
  104. if (address == null) address = IPAddress.Any;
  105. if (port < 0) throw new ArgumentOutOfRangeException(nameof(port));
  106. if (port > 65535) throw new ArgumentOutOfRangeException(nameof(port));
  107. if (_running) throw new InvalidOperationException("示例已经在运行中,无法再次启动。");
  108. _address = address;
  109. _port = port;
  110. var location = "ws://" + _address.ToString() + ":" + port.ToString();
  111. _server = new WebSocketServer(location);
  112. _server.Start(Initialize);
  113. _port = _server.Port;
  114. _running = true;
  115. }
  116. /// <summary>对所有连接发送文本。</summary>
  117. /// <exception cref="InvalidOperationException"></exception>
  118. public int Send(params char[] message)
  119. {
  120. var text = null as string;
  121. if (message == null || message.Length < 1) return 0;
  122. if (message.Length == 1)
  123. {
  124. text = message[0].ToString();
  125. }
  126. else
  127. {
  128. var sb = new System.Text.StringBuilder();
  129. foreach (var i in message)
  130. {
  131. if ((object)i != null) sb.Append(i);
  132. }
  133. text = sb.ToString();
  134. }
  135. if (text.Length < 1) return 0;
  136. var connections = GetConnections();
  137. var count = 0;
  138. foreach (var connection in connections)
  139. {
  140. try
  141. {
  142. var sent = connection.Send(message);
  143. if (sent != null) count += 1;
  144. }
  145. catch (Exception exception)
  146. {
  147. RaiseExcepted(exception);
  148. }
  149. }
  150. return count;
  151. }
  152. /// <summary>发送文本。</summary>
  153. /// <exception cref="InvalidOperationException"></exception>
  154. public int Send(params string[] message)
  155. {
  156. var text = null as string;
  157. if (message == null || message.Length < 1) return 0;
  158. if (message.Length == 1)
  159. {
  160. text = message[0].ToString();
  161. }
  162. else
  163. {
  164. var sb = new System.Text.StringBuilder();
  165. foreach (var i in message)
  166. {
  167. if (i != null) sb.Append(i);
  168. }
  169. text = sb.ToString();
  170. }
  171. if (text.Length < 1) return 0;
  172. var connections = GetConnections();
  173. var count = 0;
  174. foreach (var connection in connections)
  175. {
  176. try
  177. {
  178. var sent = connection.Send(message);
  179. if (sent != null) count += 1;
  180. }
  181. catch (Exception exception)
  182. {
  183. RaiseExcepted(exception);
  184. }
  185. }
  186. return count;
  187. }
  188. /// <summary>发送字节数组。</summary>
  189. /// <exception cref="InvalidOperationException"></exception>
  190. public int Send(params byte[] message)
  191. {
  192. var connections = GetConnections();
  193. var count = 0;
  194. foreach (var connection in connections)
  195. {
  196. try
  197. {
  198. var sent = connection.Send(message);
  199. if (sent != null) count += 1;
  200. }
  201. catch (Exception exception)
  202. {
  203. RaiseExcepted(exception);
  204. }
  205. }
  206. return count;
  207. }
  208. /// <summary>发送 PING。</summary>
  209. /// <exception cref="InvalidOperationException"></exception>
  210. public int Ping(params byte[] message)
  211. {
  212. var connections = GetConnections();
  213. var count = 0;
  214. foreach (var connection in connections)
  215. {
  216. try
  217. {
  218. var sent = connection.Ping(message);
  219. if (sent != null) count += 1;
  220. }
  221. catch (Exception exception)
  222. {
  223. RaiseExcepted(exception);
  224. }
  225. }
  226. return count;
  227. }
  228. /// <summary>发送 PONG。</summary>
  229. /// <exception cref="InvalidOperationException"></exception>
  230. public int Pong(params byte[] message)
  231. {
  232. var connections = GetConnections();
  233. var count = 0;
  234. foreach (var connection in connections)
  235. {
  236. try
  237. {
  238. var sent = connection.Pong(message);
  239. if (sent != null) count += 1;
  240. }
  241. catch (Exception exception)
  242. {
  243. RaiseExcepted(exception);
  244. }
  245. }
  246. return count;
  247. }
  248. /// <summary>关闭 Socket 连接。</summary>
  249. public void Close()
  250. {
  251. lock (_server)
  252. {
  253. try
  254. {
  255. if (_server != null && _server.ListenerSocket != null)
  256. {
  257. _server.ListenerSocket.Close();
  258. }
  259. }
  260. catch (Exception exception)
  261. {
  262. RaiseExcepted(exception);
  263. }
  264. }
  265. }
  266. #endregion
  267. #region Private
  268. List<Connection> GetConnections()
  269. {
  270. var list = new List<Connection>();
  271. lock (_connections)
  272. {
  273. foreach (var i in _connections.Values)
  274. {
  275. if (i != null) list.Add(i);
  276. }
  277. }
  278. return list;
  279. }
  280. void RaiseExcepted(Exception exception)
  281. {
  282. if (exception == null) return;
  283. if (Excepted == null) return;
  284. try
  285. {
  286. InBackground(() => Excepted(this, exception));
  287. }
  288. catch { }
  289. }
  290. void Initialize(Connection socket)
  291. {
  292. if (socket == null) return;
  293. socket.OnOpen = () => InBackground(() =>
  294. {
  295. var hashcode = socket.GetHashCode();
  296. lock (_connections)
  297. {
  298. if (!_connections.ContainsKey(hashcode))
  299. {
  300. _connections.Add(hashcode, socket);
  301. }
  302. }
  303. try
  304. {
  305. OnOpen?.Invoke(socket);
  306. }
  307. catch (Exception exception)
  308. {
  309. RaiseExcepted(exception);
  310. }
  311. });
  312. socket.OnClose = () => InBackground(() =>
  313. {
  314. var hashcode = socket.GetHashCode();
  315. try
  316. {
  317. OnClose?.Invoke(socket);
  318. }
  319. catch (Exception exception)
  320. {
  321. RaiseExcepted(exception);
  322. }
  323. });
  324. socket.OnBytes = (content) => InBackground(() =>
  325. {
  326. try
  327. {
  328. OnBytes?.Invoke(socket, content);
  329. }
  330. catch (Exception exception)
  331. {
  332. RaiseExcepted(exception);
  333. }
  334. });
  335. socket.OnError = (content) => InBackground(() =>
  336. {
  337. try
  338. {
  339. OnError?.Invoke(socket, content);
  340. }
  341. catch (Exception exception)
  342. {
  343. RaiseExcepted(exception);
  344. }
  345. });
  346. socket.OnMessage = (content) => InBackground(() =>
  347. {
  348. try
  349. {
  350. OnMessage?.Invoke(socket, content);
  351. }
  352. catch (Exception exception)
  353. {
  354. RaiseExcepted(exception);
  355. }
  356. });
  357. socket.OnPing = (content) => InBackground(() =>
  358. {
  359. try
  360. {
  361. if (OnPing == null) socket.Pong(content);
  362. else OnPing?.Invoke(socket, content);
  363. }
  364. catch (Exception exception)
  365. {
  366. RaiseExcepted(exception);
  367. }
  368. });
  369. socket.OnPong = (content) => InBackground(() =>
  370. {
  371. try
  372. {
  373. OnPong?.Invoke(socket, content);
  374. }
  375. catch (Exception exception)
  376. {
  377. RaiseExcepted(exception);
  378. }
  379. });
  380. }
  381. int GetHashCode(Connection connection)
  382. {
  383. if (connection == null) return 0;
  384. if (connection.ConnectionInfo == null) return 0;
  385. return connection.ConnectionInfo.Id.GetHashCode();
  386. }
  387. #endregion
  388. #region static
  389. /// <summary></summary>
  390. public static LogLevel LogLevel
  391. {
  392. get { return WebSocketLog.Level; }
  393. set { WebSocketLog.Level = value; }
  394. }
  395. [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
  396. private static void InBackground(System.Action action)
  397. {
  398. if (action == null) return;
  399. var thread = new System.Threading.Thread(delegate (object v) { ((System.Action)v)(); });
  400. thread.IsBackground = true;
  401. thread.Start(action);
  402. }
  403. #endregion
  404. }
  405. }
  406. #endif