From 63f18f62aa845c755321129c72c66d6d277a00ae Mon Sep 17 00:00:00 2001 From: zqm Date: Thu, 5 Feb 2026 10:15:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0WebSocket=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CubeLib/CubeLib/CubeLib.csproj | 21 +- .../CubeLib/WebSocket/IWebSocketClient.cs | 108 ++++ .../CubeLib/WebSocket/IWebSocketServer.cs | 124 +++++ .../CubeLib/WebSocket/WebSocketClient.cs | 515 ++++++++++++++++++ .../CubeLib/WebSocket/WebSocketConfig.cs | 50 ++ .../WebSocket/WebSocketErrorEventArgs.cs | 20 + .../WebSocket/WebSocketMessageEventArgs.cs | 25 + .../WebSocketMessageFailedEventArgs.cs | 30 + .../CubeLib/WebSocket/WebSocketServer.cs | 258 +++++++++ .../WebSocketStatusChangedEventArgs.cs | 20 + .../CubeLib/WebSocket/WebSocketTest.cs | 71 +++ .../CubeLib/CubeLib/packages.config | 4 + 12 files changed, 1242 insertions(+), 4 deletions(-) create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketClient.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketServer.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketClient.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketConfig.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketErrorEventArgs.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageEventArgs.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageFailedEventArgs.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketServer.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketStatusChangedEventArgs.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketTest.cs create mode 100644 Windows/CS/Framework4.0/CubeLib/CubeLib/packages.config diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/CubeLib.csproj b/Windows/CS/Framework4.0/CubeLib/CubeLib/CubeLib.csproj index 0aa0bad..bb6fabc 100644 --- a/Windows/CS/Framework4.0/CubeLib/CubeLib/CubeLib.csproj +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/CubeLib.csproj @@ -1,4 +1,4 @@ - + @@ -38,6 +38,9 @@ + + ..\packages\Newtonsoft.Json.13.0.4\lib\net40\Newtonsoft.Json.dll + @@ -45,18 +48,28 @@ - - ..\packages\WebSocketSharp-netstandard.1.0.1\lib\net35\websocket-sharp.dll + + ..\..\..\..\..\..\References\websocket-sharp\websocket-sharp\bin\Debug\websocket-sharp.dll + + + + + + + + + + - + \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketClient.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketClient.cs new file mode 100644 index 0000000..617a715 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketClient.cs @@ -0,0 +1,108 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket客户端接口 + /// + public interface IWebSocketClient + { + /// + /// 连接WebSocket服务器 + /// + void Connect(); + + /// + /// 断开WebSocket连接 + /// + void Disconnect(); + + /// + /// 重连WebSocket服务器 + /// + void Reconnect(); + + /// + /// 发送消息 + /// + /// 消息类型 + /// 消息数据 + void Send(string type, object data); + + /// + /// 发送二进制消息 + /// + /// 二进制数据 + void Send(byte[] data); + + /// + /// 获取当前连接状态 + /// + /// 连接状态 + string GetStatus(); + + /// + /// 检查是否已连接 + /// + /// 是否已连接 + bool IsConnected(); + + /// + /// 获取消息队列大小 + /// + /// 队列大小 + int GetQueueSize(); + + /// + /// 连接成功事件 + /// + event EventHandler Connected; + + /// + /// 连接断开事件 + /// + event EventHandler Disconnected; + + /// + /// 错误事件 + /// + event EventHandler Error; + + /// + /// 收到消息事件 + /// + event EventHandler Message; + + /// + /// 状态改变事件 + /// + event EventHandler StatusChanged; + + /// + /// 正在连接事件 + /// + event EventHandler Connecting; + + /// + /// 正在重连事件 + /// + event EventHandler Reconnecting; + + /// + /// 消息已发送事件 + /// + event EventHandler MessageSent; + + /// + /// 消息已加入队列事件 + /// + event EventHandler MessageQueued; + + /// + /// 消息发送失败事件 + /// + event EventHandler MessageFailed; + } + + +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketServer.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketServer.cs new file mode 100644 index 0000000..7d233c6 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/IWebSocketServer.cs @@ -0,0 +1,124 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket服务器接口 + /// + public interface IWebSocketServer + { + /// + /// 启动服务器 + /// + /// 端口号 + void Start(int port); + + /// + /// 停止服务器 + /// + void Stop(); + + /// + /// 广播消息 + /// + /// 消息类型 + /// 消息数据 + void Broadcast(string type, object data); + + /// + /// 广播二进制消息 + /// + /// 二进制数据 + void Broadcast(byte[] data); + + /// + /// 向指定客户端发送消息 + /// + /// 客户端ID + /// 消息类型 + /// 消息数据 + void SendToClient(string clientId, string type, object data); + + /// + /// 向指定客户端发送二进制消息 + /// + /// 客户端ID + /// 二进制数据 + void SendToClient(string clientId, byte[] data); + + /// + /// 检查服务器是否正在运行 + /// + /// 是否正在运行 + bool IsRunning(); + + /// + /// 获取当前连接的客户端数量 + /// + /// 客户端数量 + int GetClientCount(); + + /// + /// 客户端连接事件 + /// + event EventHandler ClientConnected; + + /// + /// 客户端断开事件 + /// + event EventHandler ClientDisconnected; + + /// + /// 收到消息事件 + /// + event EventHandler MessageReceived; + + /// + /// 错误事件 + /// + event EventHandler Error; + } + + // 事件参数类 + /// + /// 客户端连接事件参数 + /// + public class WebSocketClientConnectedEventArgs : EventArgs + { + /// + /// 客户端ID + /// + public string ClientId { get; set; } + + /// + /// 远程端点 + /// + public string RemoteEndpoint { get; set; } + } + + /// + /// 客户端断开事件参数 + /// + public class WebSocketClientDisconnectedEventArgs : EventArgs + { + /// + /// 客户端ID + /// + public string ClientId { get; set; } + + /// + /// 远程端点 + /// + public string RemoteEndpoint { get; set; } + + /// + /// 关闭代码 + /// + public int CloseCode { get; set; } + + /// + /// 关闭原因 + /// + public string CloseReason { get; set; } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketClient.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketClient.cs new file mode 100644 index 0000000..8ead3aa --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketClient.cs @@ -0,0 +1,515 @@ +using System; +using System.Collections.Generic; +using System.Timers; +using System.Threading; +using WebSocketSharp; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket客户端实现 + /// + public class WebSocketClient : IWebSocketClient + { + private WebSocketSharp.WebSocket _webSocket; + private WebSocketConfig _config; + private System.Timers.Timer _heartbeatTimer; + private System.Timers.Timer _reconnectTimer; + private System.Timers.Timer _connectTimeoutTimer; + private List _messageQueue; + private string _status = "disconnected"; + private int _reconnectAttempts = 0; + private int _reconnectDelay = 1000; + + // 事件 + /// + /// 连接成功事件 + /// + public event EventHandler Connected; + + /// + /// 连接断开事件 + /// + public event EventHandler Disconnected; + + /// + /// 错误事件 + /// + public event EventHandler Error; + + /// + /// 收到消息事件 + /// + public event EventHandler Message; + + /// + /// 状态改变事件 + /// + public event EventHandler StatusChanged; + + /// + /// 正在连接事件 + /// + public event EventHandler Connecting; + + /// + /// 正在重连事件 + /// + public event EventHandler Reconnecting; + + /// + /// 消息已发送事件 + /// + public event EventHandler MessageSent; + + /// + /// 消息已加入队列事件 + /// + public event EventHandler MessageQueued; + + /// + /// 消息发送失败事件 + /// + public event EventHandler MessageFailed; + + /// + /// 构造函数 + /// + /// 配置参数 + public WebSocketClient(WebSocketConfig config = null) + { + _config = config ?? new WebSocketConfig(); + _messageQueue = new List(); + } + + /// + /// 连接WebSocket服务器 + /// + public void Connect() + { + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Open) + { + Log("WebSocket已连接,跳过本次连接"); + return; + } + + ChangeStatus("connecting"); + Connecting?.Invoke(this, EventArgs.Empty); + + try + { + _webSocket?.Close(); + _webSocket = new WebSocketSharp.WebSocket(_config.WsUrl); + + // 设置事件处理 + _webSocket.OnOpen += WebSocket_OnOpen; + _webSocket.OnMessage += WebSocket_OnMessage; + _webSocket.OnClose += WebSocket_OnClose; + _webSocket.OnError += WebSocket_OnError; + + // 启动连接超时定时器 + StartConnectTimeoutTimer(); + + _webSocket.Connect(); + } + catch (Exception ex) + { + StopConnectTimeoutTimer(); + LogError("WebSocket连接失败: " + ex.Message); + ChangeStatus("error"); + Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = ex, Message = ex.Message }); + + if (_config.Reconnect) + { + ScheduleReconnect(); + } + } + } + + /// + /// 断开WebSocket连接 + /// + public void Disconnect() + { + CancelReconnect(); + StopHeartbeat(); + StopConnectTimeoutTimer(); + + if (_webSocket != null) + { + _webSocket.Close(); + _webSocket = null; + } + + ChangeStatus("disconnected"); + } + + /// + /// 重连WebSocket服务器 + /// + public void Reconnect() + { + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Connecting) + { + Log("WebSocket正在连接中,跳过本次重连"); + return; + } + + CancelReconnect(); + Connect(); + } + + // 消息发送 + /// + /// 发送消息 + /// + /// 消息类型 + /// 消息数据 + public void Send(string type, object data) + { + var message = new { Type = type, Data = data }; + + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Open) + { + try + { + var json = Newtonsoft.Json.JsonConvert.SerializeObject(message); + _webSocket.Send(json); + Log("发送消息: " + json); + MessageSent?.Invoke(this, new WebSocketMessageEventArgs { Type = type, Data = data }); + } + catch (Exception ex) + { + LogError("发送消息失败: " + ex.Message); + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Type = type, Data = data, Reason = "send_error", Exception = ex }); + } + } + else + { + if (_messageQueue.Count < _config.MaxQueueSize) + { + _messageQueue.Add(message); + Log("消息已加入队列: " + Newtonsoft.Json.JsonConvert.SerializeObject(message)); + MessageQueued?.Invoke(this, new WebSocketMessageEventArgs { Type = type, Data = data }); + } + else + { + LogError("消息队列已满,丢弃消息: " + Newtonsoft.Json.JsonConvert.SerializeObject(message)); + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Type = type, Data = data, Reason = "queue_full" }); + } + } + } + + /// + /// 发送二进制消息 + /// + /// 二进制数据 + public void Send(byte[] data) + { + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Open) + { + try + { + _webSocket.Send(data); + Log("发送二进制消息,长度: " + data.Length); + } + catch (Exception ex) + { + LogError("发送二进制消息失败: " + ex.Message); + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Data = data, Reason = "send_error", Exception = ex }); + } + } + else + { + // 二进制消息也加入队列 + if (_messageQueue.Count < _config.MaxQueueSize) + { + _messageQueue.Add(data); + Log("二进制消息已加入队列,长度: " + data.Length); + MessageQueued?.Invoke(this, new WebSocketMessageEventArgs { RawData = data }); + } + else + { + LogError("消息队列已满,丢弃二进制消息,长度: " + data.Length); + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Data = data, Reason = "queue_full" }); + } + } + } + + /// + /// 获取当前连接状态 + /// + /// 连接状态 + public string GetStatus() + { + return _status; + } + + /// + /// 检查是否已连接 + /// + /// 是否已连接 + public bool IsConnected() + { + return _status == "connected"; + } + + /// + /// 获取消息队列大小 + /// + /// 队列大小 + public int GetQueueSize() + { + return _messageQueue.Count; + } + + // 私有方法 + private void WebSocket_OnOpen(object sender, EventArgs e) + { + StopConnectTimeoutTimer(); + ChangeStatus("connected"); + _reconnectAttempts = 0; + _reconnectDelay = 1000; + + Log("WebSocket连接成功"); + Connected?.Invoke(this, EventArgs.Empty); + + StartHeartbeat(); + FlushQueue(); + } + + private void WebSocket_OnMessage(object sender, MessageEventArgs e) + { + if (e.IsBinary) + { + Log("收到二进制消息,长度: " + e.RawData.Length); + Message?.Invoke(this, new WebSocketMessageEventArgs { RawData = e.RawData }); + } + else + { + Log("收到文本消息: " + e.Data); + try + { + var message = Newtonsoft.Json.JsonConvert.DeserializeObject(e.Data); + Message?.Invoke(this, new WebSocketMessageEventArgs { Data = message }); + } + catch (Exception ex) + { + LogError("JSON解析错误: " + ex.Message); + // 触发MessageFailed事件让上层感知 + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Data = e.Data, Reason = "json_parse_error", Exception = ex }); + } + } + } + + private void WebSocket_OnClose(object sender, CloseEventArgs e) + { + StopConnectTimeoutTimer(); + StopHeartbeat(); + Log("WebSocket连接关闭: " + e.Code + " " + e.Reason); + Disconnected?.Invoke(this, EventArgs.Empty); + + if (!e.WasClean && _config.Reconnect) + { + ChangeStatus("reconnecting"); + Reconnecting?.Invoke(this, EventArgs.Empty); + ScheduleReconnect(); + } + else + { + ChangeStatus("disconnected"); + } + } + + private void WebSocket_OnError(object sender, ErrorEventArgs e) + { + StopConnectTimeoutTimer(); + StopHeartbeat(); + LogError("WebSocket错误: " + e.Message); + Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = e.Exception, Message = e.Message }); + + if (_config.Reconnect) + { + ChangeStatus("reconnecting"); + Reconnecting?.Invoke(this, EventArgs.Empty); + ScheduleReconnect(); + } + else + { + ChangeStatus("error"); + } + } + + private void StartHeartbeat() + { + StopHeartbeat(); + _heartbeatTimer = new System.Timers.Timer(_config.HeartbeatInterval); + _heartbeatTimer.Elapsed += (sender, e) => + { + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Open) + { + Send("ping", new { }); + } + }; + _heartbeatTimer.Start(); + } + + private void StopHeartbeat() + { + if (_heartbeatTimer != null) + { + _heartbeatTimer.Stop(); + _heartbeatTimer.Dispose(); + _heartbeatTimer = null; + Log("已停止心跳"); + } + } + + private void StartConnectTimeoutTimer() + { + StopConnectTimeoutTimer(); + _connectTimeoutTimer = new System.Timers.Timer(_config.ConnectTimeout); + _connectTimeoutTimer.Elapsed += (sender, e) => + { + if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Connecting) + { + LogError("连接超时"); + _webSocket.Close(); + Error?.Invoke(this, new WebSocketErrorEventArgs { Message = "Connection timeout" }); + } + }; + _connectTimeoutTimer.Start(); + } + + private void StopConnectTimeoutTimer() + { + if (_connectTimeoutTimer != null) + { + _connectTimeoutTimer.Stop(); + _connectTimeoutTimer.Dispose(); + _connectTimeoutTimer = null; + Log("已停止连接超时定时器"); + } + } + + private void ScheduleReconnect() + { + CancelReconnect(); + + var delay = Math.Min(_reconnectDelay, _config.MaxReconnectDelay); + Log("准备重连,延迟: " + delay + "ms"); + + _reconnectTimer = new System.Timers.Timer(delay); + _reconnectTimer.Elapsed += (sender, e) => + { + _reconnectAttempts++; + Log("开始重连,尝试次数: " + _reconnectAttempts); + Connect(); + + if (_reconnectDelay < _config.MaxReconnectDelay) + { + _reconnectDelay *= 2; + } + }; + _reconnectTimer.Start(); + } + + private void CancelReconnect() + { + if (_reconnectTimer != null) + { + _reconnectTimer.Stop(); + _reconnectTimer.Dispose(); + _reconnectTimer = null; + Log("已取消重连计划"); + } + } + + private void FlushQueue() + { + int retryCount = 0; + const int maxRetry = 3; + + while (_messageQueue.Count > 0) + { + var msg = _messageQueue[0]; + _messageQueue.RemoveAt(0); + + try + { + if (msg is byte[]) + { + // 二进制消息 + _webSocket.Send((byte[])msg); + Log("发送队列二进制消息,长度: " + ((byte[])msg).Length); + } + else + { + // 文本消息 + var json = Newtonsoft.Json.JsonConvert.SerializeObject(msg); + _webSocket.Send(json); + Log("发送队列消息: " + json); + } + + // 触发MessageSent事件 + if (msg is byte[]) + { + MessageSent?.Invoke(this, new WebSocketMessageEventArgs { RawData = (byte[])msg }); + } + else + { + MessageSent?.Invoke(this, new WebSocketMessageEventArgs { Data = msg }); + } + + retryCount = 0; // 重置重试计数 + } + catch (Exception ex) + { + retryCount++; + LogError("发送队列消息失败,重试次数: " + retryCount + "/" + maxRetry + ": " + ex.Message); + + if (retryCount < maxRetry) + { + // 重试次数未达上限,重新加入队列 + _messageQueue.Insert(0, msg); + Thread.Sleep(100 * retryCount); // 指数退避 + } + else + { + // 重试次数达上限,触发失败事件 + MessageFailed?.Invoke(this, new WebSocketMessageFailedEventArgs { Data = msg, Reason = "send_error", Exception = ex }); + retryCount = 0; // 重置重试计数 + } + + break; // 退出循环,避免持续失败 + } + } + } + + private void ChangeStatus(string newStatus) + { + if (_status == newStatus) + return; + + var oldStatus = _status; + _status = newStatus; + StatusChanged?.Invoke(this, new WebSocketStatusChangedEventArgs { OldStatus = oldStatus, NewStatus = newStatus }); + } + + private void Log(string message) + { + if (_config.DebugMode) + { + Console.WriteLine("[CubeWebSocket] " + message); + } + } + + private void LogError(string message) + { + if (_config.DebugMode) + { + Console.Error.WriteLine("[CubeWebSocket] " + message); + } + } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketConfig.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketConfig.cs new file mode 100644 index 0000000..26b640c --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketConfig.cs @@ -0,0 +1,50 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket配置类 + /// + public class WebSocketConfig + { + /// + /// WebSocket服务器URL + /// + public string WsUrl { get; set; } = "ws://localhost:8086/ws"; + + /// + /// 是否自动连接 + /// + public bool AutoConnect { get; set; } = true; + + /// + /// 是否自动重连 + /// + public bool Reconnect { get; set; } = true; + + /// + /// 最大重连延迟 + /// + public int MaxReconnectDelay { get; set; } = 30000; + + /// + /// 是否启用调试模式 + /// + public bool DebugMode { get; set; } = false; + + /// + /// 心跳间隔 + /// + public int HeartbeatInterval { get; set; } = 30000; + + /// + /// 连接超时时间 + /// + public int ConnectTimeout { get; set; } = 10000; + + /// + /// 消息队列最大大小 + /// + public int MaxQueueSize { get; set; } = 100; + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketErrorEventArgs.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketErrorEventArgs.cs new file mode 100644 index 0000000..c8ab8ac --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketErrorEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket错误事件参数 + /// + public class WebSocketErrorEventArgs : EventArgs + { + /// + /// 异常对象 + /// + public Exception Exception { get; set; } + + /// + /// 错误消息 + /// + public string Message { get; set; } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageEventArgs.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageEventArgs.cs new file mode 100644 index 0000000..82b226e --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageEventArgs.cs @@ -0,0 +1,25 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket消息事件参数 + /// + public class WebSocketMessageEventArgs : EventArgs + { + /// + /// 消息类型 + /// + public string Type { get; set; } + + /// + /// 消息数据 + /// + public object Data { get; set; } + + /// + /// 原始二进制数据 + /// + public byte[] RawData { get; set; } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageFailedEventArgs.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageFailedEventArgs.cs new file mode 100644 index 0000000..450e080 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketMessageFailedEventArgs.cs @@ -0,0 +1,30 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket消息失败事件参数 + /// + public class WebSocketMessageFailedEventArgs : EventArgs + { + /// + /// 消息类型 + /// + public string Type { get; set; } + + /// + /// 消息数据 + /// + public object Data { get; set; } + + /// + /// 失败原因 + /// + public string Reason { get; set; } + + /// + /// 异常对象 + /// + public Exception Exception { get; set; } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketServer.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketServer.cs new file mode 100644 index 0000000..261b5df --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketServer.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using WebSocketSharp.Server; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket服务器实现 + /// + public class WebSocketServer : IWebSocketServer + { + private WebSocketSharp.Server.WebSocketServer _server; + private Dictionary _sessions; + + // 事件 + /// + /// 客户端连接事件 + /// + public event EventHandler ClientConnected; + + /// + /// 客户端断开事件 + /// + public event EventHandler ClientDisconnected; + + /// + /// 收到消息事件 + /// + public event EventHandler MessageReceived; + + /// + /// 错误事件 + /// + public event EventHandler Error; + + /// + /// 构造函数 + /// + public WebSocketServer() + { + _sessions = new Dictionary(); + } + + /// + /// 启动服务器 + /// + /// 端口号 + public void Start(int port) + { + try + { + _server = new WebSocketSharp.Server.WebSocketServer(port); + _server.AddWebSocketService("/"); + + // 行为设置已在AddWebSocketService时完成 + + _server.Start(); + } + catch (Exception ex) + { + Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = ex, Message = ex.Message }); + throw; + } + } + + /// + /// 停止服务器 + /// + public void Stop() + { + if (_server != null && _server.IsListening) + { + _server.Stop(); + } + } + + /// + /// 广播消息 + /// + /// 消息类型 + /// 消息数据 + public void Broadcast(string type, object data) + { + if (_server != null) + { + _server.WebSocketServices["/"].Sessions.Broadcast(Newtonsoft.Json.JsonConvert.SerializeObject(new { Type = type, Data = data })); + } + } + + /// + /// 广播二进制消息 + /// + /// 二进制数据 + public void Broadcast(byte[] data) + { + if (_server != null) + { + _server.WebSocketServices["/"].Sessions.Broadcast(data); + } + } + + /// + /// 向指定客户端发送消息 + /// + /// 客户端ID + /// 消息类型 + /// 消息数据 + public void SendToClient(string clientId, string type, object data) + { + if (_server != null) + { + _server.WebSocketServices["/"].Sessions.SendTo(Newtonsoft.Json.JsonConvert.SerializeObject(new { Type = type, Data = data }), clientId); + } + } + + /// + /// 向指定客户端发送二进制消息 + /// + /// 客户端ID + /// 二进制数据 + public void SendToClient(string clientId, byte[] data) + { + if (_server != null) + { + _server.WebSocketServices["/"].Sessions.SendTo(data, clientId); + } + } + + /// + /// 检查服务器是否正在运行 + /// + /// 是否正在运行 + public bool IsRunning() + { + return _server != null && _server.IsListening; + } + + /// + /// 获取当前连接的客户端数量 + /// + /// 客户端数量 + public int GetClientCount() + { + return _sessions.Count; + } + + internal void OnClientConnected(EasyWsBehavior session) + { + // Replace existing session entry if ID is reused + if (_sessions.ContainsKey(session.ID)) + { + _sessions.Remove(session.ID); + } + _sessions[session.ID] = session; + + var args = new WebSocketClientConnectedEventArgs + { + ClientId = session.ID, + RemoteEndpoint = "unknown" + }; + ClientConnected?.Invoke(this, args); + } + + internal void OnClientDisconnected(EasyWsBehavior session, int closeCode, string closeReason) + { + if (_sessions.ContainsKey(session.ID)) + { + _sessions.Remove(session.ID); + + var args = new WebSocketClientDisconnectedEventArgs + { + ClientId = session.ID, + RemoteEndpoint = "unknown", + CloseCode = closeCode, + CloseReason = closeReason + }; + ClientDisconnected?.Invoke(this, args); + } + } + + internal void OnMessageReceived(EasyWsBehavior session, string message) + { + try + { + var data = Newtonsoft.Json.JsonConvert.DeserializeObject(message); + var args = new WebSocketMessageEventArgs { Data = data }; + MessageReceived?.Invoke(this, args); + } + catch (Exception ex) + { + Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = ex, Message = ex.Message }); + } + } + + internal void OnMessageReceived(EasyWsBehavior session, byte[] data) + { + var args = new WebSocketMessageEventArgs { RawData = data }; + MessageReceived?.Invoke(this, args); + } + } + + // 内部使用的WebSocket行为类 + internal class EasyWsBehavior : WebSocketBehavior + { + internal WebSocketServer Server { get; set; } + + protected override void OnOpen() + { + base.OnOpen(); + Server?.OnClientConnected(this); + } + + protected override void OnClose(WebSocketSharp.CloseEventArgs e) + { + base.OnClose(e); + Server?.OnClientDisconnected(this, e.Code, e.Reason); + } + + protected override void OnMessage(WebSocketSharp.MessageEventArgs e) + { + base.OnMessage(e); + if (e.IsBinary) + { + Server?.OnMessageReceived(this, e.RawData); + } + else + { + Server?.OnMessageReceived(this, e.Data); + } + } + + internal void Broadcast(string type, object data) + { + var message = new { Type = type, Data = data }; + var json = Newtonsoft.Json.JsonConvert.SerializeObject(message); + Sessions.Broadcast(json); + } + + internal void Broadcast(byte[] data) + { + Sessions.Broadcast(data); + } + + internal void SendToClient(string clientId, string type, object data) + { + // 直接发送,不检查会话是否存在 + var message = new { Type = type, Data = data }; + var json = Newtonsoft.Json.JsonConvert.SerializeObject(message); + Sessions.SendTo(json, clientId); + } + + internal void SendToClient(string clientId, byte[] data) + { + // 直接发送,不检查会话是否存在 + Sessions.SendTo(data, clientId); + } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketStatusChangedEventArgs.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketStatusChangedEventArgs.cs new file mode 100644 index 0000000..1c765f8 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketStatusChangedEventArgs.cs @@ -0,0 +1,20 @@ +using System; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket状态改变事件参数 + /// + public class WebSocketStatusChangedEventArgs : EventArgs + { + /// + /// 旧状态 + /// + public string OldStatus { get; set; } + + /// + /// 新状态 + /// + public string NewStatus { get; set; } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketTest.cs b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketTest.cs new file mode 100644 index 0000000..cdb57c9 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/WebSocket/WebSocketTest.cs @@ -0,0 +1,71 @@ +using System; +using System.Threading; + +namespace JoyD.Windows.CS.WebSocket +{ + /// + /// WebSocket测试类 + /// + public class WebSocketTest + { + /// + /// 测试WebSocket客户端 + /// + public static void TestClient() + { + Console.WriteLine("开始测试WebSocket客户端..."); + + var config = new WebSocketConfig + { + WsUrl = "ws://localhost:8086/ws", + DebugMode = true + }; + + var client = new WebSocketClient(config); + + // 订阅事件 + client.Connected += (sender, e) => Console.WriteLine("连接成功"); + client.Disconnected += (sender, e) => Console.WriteLine("连接断开"); + client.Error += (sender, e) => Console.WriteLine("错误: " + e.Message); + client.Message += (sender, e) => Console.WriteLine("收到消息: " + e.Data); + + // 连接服务器 + client.Connect(); + + // 发送测试消息 + Thread.Sleep(1000); + client.Send("test", new { Message = "Hello, WebSocket!" }); + + Console.WriteLine("按任意键退出..."); + Console.ReadKey(); + + // 断开连接 + client.Disconnect(); + } + + /// + /// 测试WebSocket服务器 + /// + public static void TestServer() + { + Console.WriteLine("开始测试WebSocket服务器..."); + + var server = new WebSocketServer(); + + // 订阅事件 + server.ClientConnected += (sender, e) => Console.WriteLine("客户端连接: " + e.ClientId); + server.ClientDisconnected += (sender, e) => Console.WriteLine("客户端断开: " + e.ClientId); + server.MessageReceived += (sender, e) => Console.WriteLine("收到消息: " + e.Data); + + // 启动服务器 + server.Start(8086); + Console.WriteLine("服务器已启动,端口: 8086"); + + Console.WriteLine("按任意键停止服务器..."); + Console.ReadKey(); + + // 停止服务器 + server.Stop(); + } + } +} \ No newline at end of file diff --git a/Windows/CS/Framework4.0/CubeLib/CubeLib/packages.config b/Windows/CS/Framework4.0/CubeLib/CubeLib/packages.config new file mode 100644 index 0000000..649c3f8 --- /dev/null +++ b/Windows/CS/Framework4.0/CubeLib/CubeLib/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file