|
|
#if !NET20
using Apewer; using Apewer.Network; using System; using System.Collections.Generic; using System.Net;
namespace Apewer.WebSocket {
/// <summary></summary>
public sealed class ChatServer {
#region public
/// <summary></summary>
public Action<string> ErrorAction { get; set; }
/// <summary></summary>
public Action<Exception> ExceptedAction { get; set; }
/// <summary></summary>
public Action DisposedAction { get; set; }
/// <summary></summary>
public bool WebSocketListening { get { return (_websocket != null && _websocket.Running); } }
/// <summary>WebSocket 监听的地址,默认为 0.0.0.0。</summary>
public string WebSocketAddress { get { return _websocketaddress; } set { if (!WebSocketListening) _websocketaddress = value; } }
/// <summary>WebSocket 监听的端口,默认为 8000。</summary>
public int WebSocketPort { get { return _websocketport; } set { if (!WebSocketListening) _websocketport = NumberUtility.Restrict(value, 0, ushort.MaxValue); } }
/// <summary></summary>
public bool CommandListening { get { return false; } }
/// <summary>命令服务监听的地址,默认为 0.0.0.0。</summary>
public string CommandAddress { get { return _commandaddress; } set { if (!CommandListening) _commandaddress = value; } }
/// <summary>命令服务监听的端口,默认为 8000。</summary>
public int CommandPort { get { return _commandport; } set { if (!CommandListening) _commandport = NumberUtility.Restrict(value, 0, ushort.MaxValue); } }
/// <summary></summary>
public void Start() { PrivateStart(); }
/// <summary></summary>
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<int, List<Connection>> _rooms = new Dictionary<int, List<Connection>>();
private List<Connection> _lobby = new List<Connection>();
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<Connection> 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<Connection>(); if (room == 0 && !TextUtility.IsBlank(caption)) room = caption.GetHashCode(); return room; } return 0; }
List<Connection> JoinRoom(Connection socket, int room) { var connections = new List<Connection>(); 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<Connection>(); value.Add(socket); connections.AddRange(value); _rooms.Add(room, value); } } } return connections; }
List<Connection> LeaveRoom(Connection socket, int room) { var connections = new List<Connection>(); 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
/// <summary></summary>
public static LogLevel LogLevel { get { return GenericServer.LogLevel; } set { GenericServer.LogLevel = value; } }
#endregion
#region program
/// <summary>运行示例实例。</summary>
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
|