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.

444 lines
14 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. #if !NET20
  2. using Apewer;
  3. using Apewer.Network;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Net;
  7. namespace Apewer.WebSocket
  8. {
  9. /// <summary></summary>
  10. public sealed class ChatServer
  11. {
  12. #region public
  13. /// <summary></summary>
  14. public Action<string> ErrorAction { get; set; }
  15. /// <summary></summary>
  16. public Action<Exception> ExceptedAction { get; set; }
  17. /// <summary></summary>
  18. public Action DisposedAction { get; set; }
  19. /// <summary></summary>
  20. public bool WebSocketListening
  21. {
  22. get { return (_websocket != null && _websocket.Running); }
  23. }
  24. /// <summary>WebSocket 监听的地址,默认为 0.0.0.0。</summary>
  25. public string WebSocketAddress
  26. {
  27. get { return _websocketaddress; }
  28. set { if (!WebSocketListening) _websocketaddress = value; }
  29. }
  30. /// <summary>WebSocket 监听的端口,默认为 8000。</summary>
  31. public int WebSocketPort
  32. {
  33. get { return _websocketport; }
  34. set { if (!WebSocketListening) _websocketport = NumberUtility.Restrict(value, 0, ushort.MaxValue); }
  35. }
  36. /// <summary></summary>
  37. public bool CommandListening
  38. {
  39. get { return false; }
  40. }
  41. /// <summary>命令服务监听的地址,默认为 0.0.0.0。</summary>
  42. public string CommandAddress
  43. {
  44. get { return _commandaddress; }
  45. set { if (!CommandListening) _commandaddress = value; }
  46. }
  47. /// <summary>命令服务监听的端口,默认为 8000。</summary>
  48. public int CommandPort
  49. {
  50. get { return _commandport; }
  51. set { if (!CommandListening) _commandport = NumberUtility.Restrict(value, 0, ushort.MaxValue); }
  52. }
  53. /// <summary></summary>
  54. public void Start() { PrivateStart(); }
  55. /// <summary></summary>
  56. public void Dispose()
  57. {
  58. if (_websocket != null)
  59. {
  60. lock (_websocket)
  61. {
  62. _websocket.Close();
  63. _websocket.Dispose();
  64. }
  65. _websocket = null;
  66. }
  67. DisposedAction?.Invoke();
  68. }
  69. #endregion
  70. #region fields
  71. private string _websocketaddress = "0.0.0.0";
  72. private int _websocketport = 8000;
  73. private string _commandaddress = "0.0.0.0";
  74. private int _commandport = 8000;
  75. private Dictionary<int, List<Connection>> _rooms = new Dictionary<int, List<Connection>>();
  76. private List<Connection> _lobby = new List<Connection>();
  77. private GenericServer _websocket = null;
  78. private UdpServer _command = null;
  79. #endregion
  80. #region private
  81. void RaiseError(params string[] text)
  82. {
  83. var merged = TextUtility.Merge(text);
  84. if (!TextUtility.IsBlank(merged)) ErrorAction?.Invoke(merged);
  85. }
  86. void RaiseExcepted(Exception exception)
  87. {
  88. if (exception != null) ExceptedAction?.Invoke(exception);
  89. }
  90. void RaiseConsole(params string[] text)
  91. {
  92. var merged = TextUtility.Merge(text);
  93. if (!TextUtility.IsBlank(merged)) Logger.Web.Text(this, merged);
  94. }
  95. private void PrivateStart()
  96. {
  97. if (_websocket != null) return;
  98. _websocket = new GenericServer();
  99. lock (_websocket)
  100. {
  101. try
  102. {
  103. _websocket.Start(_websocketport, IPAddress.Parse(_websocketaddress));
  104. _websocket.OnMessage += WebSocketReceived;
  105. _websocket.OnOpen += WebsocketOnOpen;
  106. _websocket.OnClose += WebsocketOnClose;
  107. RaiseConsole("WebSocket 服务已启动。");
  108. }
  109. catch (Exception ex)
  110. {
  111. _websocket = null;
  112. RaiseError($"WebSocket 服务启动失败:{ex.Message()}");
  113. return;
  114. }
  115. _command = new UdpServer();
  116. _command.Port = _commandport;
  117. _command.Excepted += (s, ex) => RaiseExcepted(ex);
  118. _command.Quitted += (s) => RaiseConsole("命令服务已退出。");
  119. _command.Started += (s) => RaiseConsole("命令服务已启动。");
  120. _command.Received += (s, e) => CommandReceived(s, e.IP, e.Port, e.Bytes);
  121. _command.Start();
  122. }
  123. }
  124. void WebsocketOnClose(Connection socket)
  125. {
  126. var room = GetRoomId(socket.QueryPath);
  127. var connections = LeaveRoom(socket, room);
  128. Send(connections, PackLeft(socket, room).ToString());
  129. }
  130. void WebsocketOnOpen(Connection socket)
  131. {
  132. var room = GetRoomId(socket.QueryPath);
  133. var connections = JoinRoom(socket, room);
  134. Send(connections, PackJoined(socket, room).ToString());
  135. }
  136. void WebSocketReceived(Connection socket, string content)
  137. {
  138. if (socket == null || string.IsNullOrEmpty(content))
  139. {
  140. RaiseConsole("WebSocket 服务接收到空消息。");
  141. return;
  142. }
  143. var input = Json.From(content);
  144. if (input == null)
  145. {
  146. RaiseConsole("WebSocket 服务接收到无效消息。");
  147. return;
  148. }
  149. var room = GetRoomId(socket.QueryPath);
  150. switch (input["type"])
  151. {
  152. case "command":
  153. {
  154. switch (input["command"])
  155. {
  156. case "exit":
  157. Dispose();
  158. break;
  159. }
  160. }
  161. break;
  162. case "broadcast":
  163. {
  164. var json = PackMessage(socket, 0, input["text"], input.GetProperty("addition"));
  165. if (_websocket != null)
  166. {
  167. lock (_websocket)
  168. {
  169. _websocket.Send(json.ToString());
  170. }
  171. }
  172. }
  173. break;
  174. case "message":
  175. {
  176. var connections = JoinRoom(socket, room);
  177. var json = PackMessage(socket, room, input["text"], input.GetProperty("addition"));
  178. Send(connections, json.ToString());
  179. }
  180. break;
  181. }
  182. }
  183. void Send(List<Connection> connections, string message)
  184. {
  185. if (connections == null) return;
  186. if (string.IsNullOrEmpty(message)) return;
  187. foreach (var connection in connections)
  188. {
  189. if (connection == null) continue;
  190. try
  191. {
  192. connection.Send(message);
  193. }
  194. catch (Exception exception)
  195. {
  196. if (ExceptedAction == null)
  197. {
  198. var a = connection.Address;
  199. var p = connection.Port.ToString();
  200. var e = exception.ToString();
  201. Logger.Web.Error(this, TextUtility.Merge($"对 {a}:{p} 发送消息失败。"), exception.Message);
  202. }
  203. else
  204. {
  205. ExceptedAction(exception);
  206. }
  207. }
  208. }
  209. }
  210. int GetRoomId(string path)
  211. {
  212. if (TextUtility.IsBlank(path)) return 0;
  213. var array = path.Split('/');
  214. if (array.Length > 1)
  215. {
  216. var caption = array[1];
  217. var room = NumberUtility.Int32(caption);
  218. var connections = new List<Connection>();
  219. if (room == 0 && !TextUtility.IsBlank(caption)) room = caption.GetHashCode();
  220. return room;
  221. }
  222. return 0;
  223. }
  224. List<Connection> JoinRoom(Connection socket, int room)
  225. {
  226. var connections = new List<Connection>();
  227. if (room == 0)
  228. {
  229. lock (_lobby)
  230. {
  231. if (!_lobby.Contains(socket)) _lobby.Add(socket);
  232. connections.AddRange(_lobby);
  233. }
  234. }
  235. else
  236. {
  237. lock (_rooms)
  238. {
  239. if (_rooms.ContainsKey(room))
  240. {
  241. var value = _rooms[room];
  242. lock (value)
  243. {
  244. if (!value.Contains(socket)) value.Add(socket);
  245. connections.AddRange(value);
  246. }
  247. }
  248. else
  249. {
  250. var value = new List<Connection>();
  251. value.Add(socket);
  252. connections.AddRange(value);
  253. _rooms.Add(room, value);
  254. }
  255. }
  256. }
  257. return connections;
  258. }
  259. List<Connection> LeaveRoom(Connection socket, int room)
  260. {
  261. var connections = new List<Connection>();
  262. if (room == 0)
  263. {
  264. lock (_lobby)
  265. {
  266. if (_lobby.Contains(socket)) _lobby.Remove(socket);
  267. connections.AddRange(_lobby);
  268. }
  269. }
  270. else
  271. {
  272. lock (_rooms)
  273. {
  274. if (_rooms.ContainsKey(room))
  275. {
  276. var value = _rooms[room];
  277. var empty = false;
  278. lock (value)
  279. {
  280. if (value.Contains(socket)) value.Remove(socket);
  281. empty = value.Count == 0;
  282. if (!empty) connections.AddRange(value);
  283. if (empty) _rooms.Remove(room);
  284. }
  285. }
  286. }
  287. }
  288. return connections;
  289. }
  290. Json PackJoined(Connection socket, int room = 0)
  291. {
  292. var json = Json.NewObject();
  293. json.SetProperty("utcstamp", ClockUtility.UtcStamp);
  294. json.SetProperty("nowstamp", ClockUtility.NowStamp);
  295. json.SetProperty("nowlucid", ClockUtility.LucidNow);
  296. json.SetProperty("address", socket.Address);
  297. json.SetProperty("port", socket.Port);
  298. json.SetProperty("room", room);
  299. json.SetProperty("type", "joined");
  300. return json;
  301. }
  302. Json PackLeft(Connection socket, int room = 0)
  303. {
  304. var json = Json.NewObject();
  305. json.SetProperty("utcstamp", ClockUtility.UtcStamp);
  306. json.SetProperty("nowstamp", ClockUtility.NowStamp);
  307. json.SetProperty("nowlucid", ClockUtility.LucidNow);
  308. json.SetProperty("address", socket.Address);
  309. json.SetProperty("port", socket.Port);
  310. json.SetProperty("room", room);
  311. json.SetProperty("type", "left");
  312. return json;
  313. }
  314. Json PackMessage(Connection socket, int room = 0, string text = null, Json addition = null)
  315. {
  316. var json = Json.NewObject();
  317. json.SetProperty("utcstamp", ClockUtility.UtcStamp);
  318. json.SetProperty("nowstamp", ClockUtility.NowStamp);
  319. json.SetProperty("nowlucid", ClockUtility.LucidNow);
  320. json.SetProperty("address", socket.Address);
  321. json.SetProperty("port", socket.Port);
  322. json.SetProperty("room", room);
  323. json.SetProperty("type", "message");
  324. json.SetProperty("text", text);
  325. json.SetProperty("addition", addition);
  326. return json;
  327. }
  328. void CommandReceived(object sender, string ip, int port, byte[] bytes)
  329. {
  330. }
  331. #endregion
  332. #region static
  333. /// <summary></summary>
  334. public static LogLevel LogLevel
  335. {
  336. get { return GenericServer.LogLevel; }
  337. set { GenericServer.LogLevel = value; }
  338. }
  339. #endregion
  340. #region program
  341. /// <summary>运行示例实例。</summary>
  342. public static void RunDemo(int chatTcpPort = 8000, int commandUdpPort = 8000)
  343. {
  344. var cs = new ChatServer();
  345. cs.WebSocketPort = chatTcpPort;
  346. cs.CommandPort = commandUdpPort;
  347. cs.Start();
  348. ChatServer.LogLevel = LogLevel.Debug;
  349. while (true)
  350. {
  351. var input = Console.ReadLine();
  352. if (input == "exit") break;
  353. }
  354. cs.Dispose();
  355. }
  356. static void RunLite()
  357. {
  358. var ws = new GenericServer();
  359. ws.OnOpen += (s) =>
  360. {
  361. ws.Send(s.Address, ":", s.Port.ToString(), "\n已连接\nHost: ", s.Host, "\nOrigin: ", s.Origin + "\nPath: ", s.QueryPath);
  362. };
  363. ws.OnClose += (s) =>
  364. {
  365. ws.Send(s.Address, ":", s.Port.ToString(), "\n已断开。");
  366. };
  367. ws.OnMessage += (s, m) =>
  368. {
  369. s.Send(s.Address, ":", s.Port.ToString(), "\n", m);
  370. };
  371. ws.Start();
  372. while (true)
  373. {
  374. var input = Console.ReadLine();
  375. if (input == "exit") break;
  376. ws.Send(ws.Address.ToString(), ":", ws.Port.ToString(), "\n", input);
  377. }
  378. ws.Close();
  379. ws.Dispose();
  380. }
  381. #endregion
  382. }
  383. }
  384. #endif