#if !NET20 using Apewer; using Apewer.Network; using System; using System.Collections.Generic; using System.Net; namespace Apewer.WebSocket { /// public sealed class ChatServer { #region public /// public Action ErrorAction { get; set; } /// public Action ExceptedAction { get; set; } /// public Action DisposedAction { get; set; } /// public bool WebSocketListening { get { return (_websocket != null && _websocket.Running); } } /// WebSocket 监听的地址,默认为 0.0.0.0。 public string WebSocketAddress { get { return _websocketaddress; } set { if (!WebSocketListening) _websocketaddress = value; } } /// WebSocket 监听的端口,默认为 8000。 public int WebSocketPort { get { return _websocketport; } set { if (!WebSocketListening) _websocketport = NumberUtility.Restrict(value, 0, ushort.MaxValue); } } /// public bool CommandListening { get { return false; } } /// 命令服务监听的地址,默认为 0.0.0.0。 public string CommandAddress { get { return _commandaddress; } set { if (!CommandListening) _commandaddress = value; } } /// 命令服务监听的端口,默认为 8000。 public int CommandPort { get { return _commandport; } set { if (!CommandListening) _commandport = NumberUtility.Restrict(value, 0, ushort.MaxValue); } } /// public void Start() { PrivateStart(); } /// public void Dispose() { if (_websocket != null) { lock (_websocket) { _websocket.Close(); _websocket.Dispose(); } _websocket = null; } DisposedAction?.Invoke(); } #endregion #region fields private string _websocketaddress = "0.0.0.0"; private int _websocketport = 8000; private string _commandaddress = "0.0.0.0"; private int _commandport = 8000; private Dictionary> _rooms = new Dictionary>(); private List _lobby = new List(); private GenericServer _websocket = null; private UdpServer _command = null; #endregion #region private void RaiseError(params string[] text) { var merged = TextUtility.Merge(text); if (!TextUtility.IsBlank(merged)) ErrorAction?.Invoke(merged); } void RaiseExcepted(Exception exception) { if (exception != null) ExceptedAction?.Invoke(exception); } void RaiseConsole(params string[] text) { var merged = TextUtility.Merge(text); if (!TextUtility.IsBlank(merged)) Logger.Web.Text(this, merged); } private void PrivateStart() { if (_websocket != null) return; _websocket = new GenericServer(); lock (_websocket) { try { _websocket.Start(_websocketport, IPAddress.Parse(_websocketaddress)); _websocket.OnMessage += WebSocketReceived; _websocket.OnOpen += WebsocketOnOpen; _websocket.OnClose += WebsocketOnClose; RaiseConsole("WebSocket 服务已启动。"); } catch (Exception ex) { _websocket = null; RaiseError($"WebSocket 服务启动失败:{ex.Message()}"); return; } _command = new UdpServer(); _command.Port = _commandport; _command.Excepted += (s, ex) => RaiseExcepted(ex); _command.Quitted += (s) => RaiseConsole("命令服务已退出。"); _command.Started += (s) => RaiseConsole("命令服务已启动。"); _command.Received += (s, e) => CommandReceived(s, e.IP, e.Port, e.Bytes); _command.Start(); } } void WebsocketOnClose(Connection socket) { var room = GetRoomId(socket.QueryPath); var connections = LeaveRoom(socket, room); Send(connections, PackLeft(socket, room).ToString()); } void WebsocketOnOpen(Connection socket) { var room = GetRoomId(socket.QueryPath); var connections = JoinRoom(socket, room); Send(connections, PackJoined(socket, room).ToString()); } void WebSocketReceived(Connection socket, string content) { if (socket == null || string.IsNullOrEmpty(content)) { RaiseConsole("WebSocket 服务接收到空消息。"); return; } var input = Json.From(content); if (input == null) { RaiseConsole("WebSocket 服务接收到无效消息。"); return; } var room = GetRoomId(socket.QueryPath); switch (input["type"]) { case "command": { switch (input["command"]) { case "exit": Dispose(); break; } } break; case "broadcast": { var json = PackMessage(socket, 0, input["text"], input.GetProperty("addition")); if (_websocket != null) { lock (_websocket) { _websocket.Send(json.ToString()); } } } break; case "message": { var connections = JoinRoom(socket, room); var json = PackMessage(socket, room, input["text"], input.GetProperty("addition")); Send(connections, json.ToString()); } break; } } void Send(List connections, string message) { if (connections == null) return; if (string.IsNullOrEmpty(message)) return; foreach (var connection in connections) { if (connection == null) continue; try { connection.Send(message); } catch (Exception exception) { if (ExceptedAction == null) { var a = connection.Address; var p = connection.Port.ToString(); var e = exception.ToString(); Logger.Web.Error(this, TextUtility.Merge($"对 {a}:{p} 发送消息失败。"), exception.Message); } else { ExceptedAction(exception); } } } } int GetRoomId(string path) { if (TextUtility.IsBlank(path)) return 0; var array = path.Split('/'); if (array.Length > 1) { var caption = array[1]; var room = NumberUtility.Int32(caption); var connections = new List(); if (room == 0 && !TextUtility.IsBlank(caption)) room = caption.GetHashCode(); return room; } return 0; } List JoinRoom(Connection socket, int room) { var connections = new List(); if (room == 0) { lock (_lobby) { if (!_lobby.Contains(socket)) _lobby.Add(socket); connections.AddRange(_lobby); } } else { lock (_rooms) { if (_rooms.ContainsKey(room)) { var value = _rooms[room]; lock (value) { if (!value.Contains(socket)) value.Add(socket); connections.AddRange(value); } } else { var value = new List(); value.Add(socket); connections.AddRange(value); _rooms.Add(room, value); } } } return connections; } List LeaveRoom(Connection socket, int room) { var connections = new List(); if (room == 0) { lock (_lobby) { if (_lobby.Contains(socket)) _lobby.Remove(socket); connections.AddRange(_lobby); } } else { lock (_rooms) { if (_rooms.ContainsKey(room)) { var value = _rooms[room]; var empty = false; lock (value) { if (value.Contains(socket)) value.Remove(socket); empty = value.Count == 0; if (!empty) connections.AddRange(value); if (empty) _rooms.Remove(room); } } } } return connections; } Json PackJoined(Connection socket, int room = 0) { var json = Json.NewObject(); json.SetProperty("utcstamp", ClockUtility.UtcStamp); json.SetProperty("nowstamp", ClockUtility.NowStamp); json.SetProperty("nowlucid", ClockUtility.LucidNow); json.SetProperty("address", socket.Address); json.SetProperty("port", socket.Port); json.SetProperty("room", room); json.SetProperty("type", "joined"); return json; } Json PackLeft(Connection socket, int room = 0) { var json = Json.NewObject(); json.SetProperty("utcstamp", ClockUtility.UtcStamp); json.SetProperty("nowstamp", ClockUtility.NowStamp); json.SetProperty("nowlucid", ClockUtility.LucidNow); json.SetProperty("address", socket.Address); json.SetProperty("port", socket.Port); json.SetProperty("room", room); json.SetProperty("type", "left"); return json; } Json PackMessage(Connection socket, int room = 0, string text = null, Json addition = null) { var json = Json.NewObject(); json.SetProperty("utcstamp", ClockUtility.UtcStamp); json.SetProperty("nowstamp", ClockUtility.NowStamp); json.SetProperty("nowlucid", ClockUtility.LucidNow); json.SetProperty("address", socket.Address); json.SetProperty("port", socket.Port); json.SetProperty("room", room); json.SetProperty("type", "message"); json.SetProperty("text", text); json.SetProperty("addition", addition); return json; } void CommandReceived(object sender, string ip, int port, byte[] bytes) { } #endregion #region static /// public static LogLevel LogLevel { get { return GenericServer.LogLevel; } set { GenericServer.LogLevel = value; } } #endregion #region program /// 运行示例实例。 public static void RunDemo(int chatTcpPort = 8000, int commandUdpPort = 8000) { var cs = new ChatServer(); cs.WebSocketPort = chatTcpPort; cs.CommandPort = commandUdpPort; cs.Start(); ChatServer.LogLevel = LogLevel.Debug; while (true) { var input = Console.ReadLine(); if (input == "exit") break; } cs.Dispose(); } static void RunLite() { var ws = new GenericServer(); ws.OnOpen += (s) => { ws.Send(s.Address, ":", s.Port.ToString(), "\n已连接\nHost: ", s.Host, "\nOrigin: ", s.Origin + "\nPath: ", s.QueryPath); }; ws.OnClose += (s) => { ws.Send(s.Address, ":", s.Port.ToString(), "\n已断开。"); }; ws.OnMessage += (s, m) => { s.Send(s.Address, ":", s.Port.ToString(), "\n", m); }; ws.Start(); while (true) { var input = Console.ReadLine(); if (input == "exit") break; ws.Send(ws.Address.ToString(), ":", ws.Port.ToString(), "\n", input); } ws.Close(); ws.Dispose(); } #endregion } } #endif