实现WebSocket功能

This commit is contained in:
zqm
2026-02-05 10:15:30 +08:00
parent f51252eca2
commit 63f18f62aa
12 changed files with 1242 additions and 4 deletions

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@@ -38,6 +38,9 @@
</ApplicationIcon> </ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.4\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@@ -45,18 +48,28 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="websocket-sharp, Version=1.0.1.0, Culture=neutral, PublicKeyToken=5660b08a1845a91e, processorArchitecture=MSIL"> <Reference Include="websocket-sharp">
<HintPath>..\packages\WebSocketSharp-netstandard.1.0.1\lib\net35\websocket-sharp.dll</HintPath> <HintPath>..\..\..\..\..\..\References\websocket-sharp\websocket-sharp\bin\Debug\websocket-sharp.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WebSocket\IWebSocketClient.cs" />
<Compile Include="WebSocket\IWebSocketServer.cs" />
<Compile Include="WebSocket\WebSocketConfig.cs" />
<Compile Include="WebSocket\WebSocketClient.cs" />
<Compile Include="WebSocket\WebSocketServer.cs" />
<Compile Include="WebSocket\WebSocketTest.cs" />
<Compile Include="WebSocket\WebSocketMessageEventArgs.cs" />
<Compile Include="WebSocket\WebSocketErrorEventArgs.cs" />
<Compile Include="WebSocket\WebSocketStatusChangedEventArgs.cs" />
<Compile Include="WebSocket\WebSocketMessageFailedEventArgs.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="CubeLib.ico" /> <Content Include="CubeLib.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="WebSocket\" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@@ -0,0 +1,108 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket客户端接口
/// </summary>
public interface IWebSocketClient
{
/// <summary>
/// 连接WebSocket服务器
/// </summary>
void Connect();
/// <summary>
/// 断开WebSocket连接
/// </summary>
void Disconnect();
/// <summary>
/// 重连WebSocket服务器
/// </summary>
void Reconnect();
/// <summary>
/// 发送消息
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
void Send(string type, object data);
/// <summary>
/// 发送二进制消息
/// </summary>
/// <param name="data">二进制数据</param>
void Send(byte[] data);
/// <summary>
/// 获取当前连接状态
/// </summary>
/// <returns>连接状态</returns>
string GetStatus();
/// <summary>
/// 检查是否已连接
/// </summary>
/// <returns>是否已连接</returns>
bool IsConnected();
/// <summary>
/// 获取消息队列大小
/// </summary>
/// <returns>队列大小</returns>
int GetQueueSize();
/// <summary>
/// 连接成功事件
/// </summary>
event EventHandler Connected;
/// <summary>
/// 连接断开事件
/// </summary>
event EventHandler Disconnected;
/// <summary>
/// 错误事件
/// </summary>
event EventHandler<WebSocketErrorEventArgs> Error;
/// <summary>
/// 收到消息事件
/// </summary>
event EventHandler<WebSocketMessageEventArgs> Message;
/// <summary>
/// 状态改变事件
/// </summary>
event EventHandler<WebSocketStatusChangedEventArgs> StatusChanged;
/// <summary>
/// 正在连接事件
/// </summary>
event EventHandler Connecting;
/// <summary>
/// 正在重连事件
/// </summary>
event EventHandler Reconnecting;
/// <summary>
/// 消息已发送事件
/// </summary>
event EventHandler<WebSocketMessageEventArgs> MessageSent;
/// <summary>
/// 消息已加入队列事件
/// </summary>
event EventHandler<WebSocketMessageEventArgs> MessageQueued;
/// <summary>
/// 消息发送失败事件
/// </summary>
event EventHandler<WebSocketMessageFailedEventArgs> MessageFailed;
}
}

View File

@@ -0,0 +1,124 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket服务器接口
/// </summary>
public interface IWebSocketServer
{
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="port">端口号</param>
void Start(int port);
/// <summary>
/// 停止服务器
/// </summary>
void Stop();
/// <summary>
/// 广播消息
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
void Broadcast(string type, object data);
/// <summary>
/// 广播二进制消息
/// </summary>
/// <param name="data">二进制数据</param>
void Broadcast(byte[] data);
/// <summary>
/// 向指定客户端发送消息
/// </summary>
/// <param name="clientId">客户端ID</param>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
void SendToClient(string clientId, string type, object data);
/// <summary>
/// 向指定客户端发送二进制消息
/// </summary>
/// <param name="clientId">客户端ID</param>
/// <param name="data">二进制数据</param>
void SendToClient(string clientId, byte[] data);
/// <summary>
/// 检查服务器是否正在运行
/// </summary>
/// <returns>是否正在运行</returns>
bool IsRunning();
/// <summary>
/// 获取当前连接的客户端数量
/// </summary>
/// <returns>客户端数量</returns>
int GetClientCount();
/// <summary>
/// 客户端连接事件
/// </summary>
event EventHandler<WebSocketClientConnectedEventArgs> ClientConnected;
/// <summary>
/// 客户端断开事件
/// </summary>
event EventHandler<WebSocketClientDisconnectedEventArgs> ClientDisconnected;
/// <summary>
/// 收到消息事件
/// </summary>
event EventHandler<WebSocketMessageEventArgs> MessageReceived;
/// <summary>
/// 错误事件
/// </summary>
event EventHandler<WebSocketErrorEventArgs> Error;
}
// 事件参数类
/// <summary>
/// 客户端连接事件参数
/// </summary>
public class WebSocketClientConnectedEventArgs : EventArgs
{
/// <summary>
/// 客户端ID
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// 远程端点
/// </summary>
public string RemoteEndpoint { get; set; }
}
/// <summary>
/// 客户端断开事件参数
/// </summary>
public class WebSocketClientDisconnectedEventArgs : EventArgs
{
/// <summary>
/// 客户端ID
/// </summary>
public string ClientId { get; set; }
/// <summary>
/// 远程端点
/// </summary>
public string RemoteEndpoint { get; set; }
/// <summary>
/// 关闭代码
/// </summary>
public int CloseCode { get; set; }
/// <summary>
/// 关闭原因
/// </summary>
public string CloseReason { get; set; }
}
}

View File

@@ -0,0 +1,515 @@
using System;
using System.Collections.Generic;
using System.Timers;
using System.Threading;
using WebSocketSharp;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket客户端实现
/// </summary>
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<object> _messageQueue;
private string _status = "disconnected";
private int _reconnectAttempts = 0;
private int _reconnectDelay = 1000;
// 事件
/// <summary>
/// 连接成功事件
/// </summary>
public event EventHandler Connected;
/// <summary>
/// 连接断开事件
/// </summary>
public event EventHandler Disconnected;
/// <summary>
/// 错误事件
/// </summary>
public event EventHandler<WebSocketErrorEventArgs> Error;
/// <summary>
/// 收到消息事件
/// </summary>
public event EventHandler<WebSocketMessageEventArgs> Message;
/// <summary>
/// 状态改变事件
/// </summary>
public event EventHandler<WebSocketStatusChangedEventArgs> StatusChanged;
/// <summary>
/// 正在连接事件
/// </summary>
public event EventHandler Connecting;
/// <summary>
/// 正在重连事件
/// </summary>
public event EventHandler Reconnecting;
/// <summary>
/// 消息已发送事件
/// </summary>
public event EventHandler<WebSocketMessageEventArgs> MessageSent;
/// <summary>
/// 消息已加入队列事件
/// </summary>
public event EventHandler<WebSocketMessageEventArgs> MessageQueued;
/// <summary>
/// 消息发送失败事件
/// </summary>
public event EventHandler<WebSocketMessageFailedEventArgs> MessageFailed;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="config">配置参数</param>
public WebSocketClient(WebSocketConfig config = null)
{
_config = config ?? new WebSocketConfig();
_messageQueue = new List<object>();
}
/// <summary>
/// 连接WebSocket服务器
/// </summary>
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();
}
}
}
/// <summary>
/// 断开WebSocket连接
/// </summary>
public void Disconnect()
{
CancelReconnect();
StopHeartbeat();
StopConnectTimeoutTimer();
if (_webSocket != null)
{
_webSocket.Close();
_webSocket = null;
}
ChangeStatus("disconnected");
}
/// <summary>
/// 重连WebSocket服务器
/// </summary>
public void Reconnect()
{
if (_webSocket != null && _webSocket.ReadyState == WebSocketSharp.WebSocketState.Connecting)
{
Log("WebSocket正在连接中跳过本次重连");
return;
}
CancelReconnect();
Connect();
}
// 消息发送
/// <summary>
/// 发送消息
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
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" });
}
}
}
/// <summary>
/// 发送二进制消息
/// </summary>
/// <param name="data">二进制数据</param>
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" });
}
}
}
/// <summary>
/// 获取当前连接状态
/// </summary>
/// <returns>连接状态</returns>
public string GetStatus()
{
return _status;
}
/// <summary>
/// 检查是否已连接
/// </summary>
/// <returns>是否已连接</returns>
public bool IsConnected()
{
return _status == "connected";
}
/// <summary>
/// 获取消息队列大小
/// </summary>
/// <returns>队列大小</returns>
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);
}
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket配置类
/// </summary>
public class WebSocketConfig
{
/// <summary>
/// WebSocket服务器URL
/// </summary>
public string WsUrl { get; set; } = "ws://localhost:8086/ws";
/// <summary>
/// 是否自动连接
/// </summary>
public bool AutoConnect { get; set; } = true;
/// <summary>
/// 是否自动重连
/// </summary>
public bool Reconnect { get; set; } = true;
/// <summary>
/// 最大重连延迟
/// </summary>
public int MaxReconnectDelay { get; set; } = 30000;
/// <summary>
/// 是否启用调试模式
/// </summary>
public bool DebugMode { get; set; } = false;
/// <summary>
/// 心跳间隔
/// </summary>
public int HeartbeatInterval { get; set; } = 30000;
/// <summary>
/// 连接超时时间
/// </summary>
public int ConnectTimeout { get; set; } = 10000;
/// <summary>
/// 消息队列最大大小
/// </summary>
public int MaxQueueSize { get; set; } = 100;
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket错误事件参数
/// </summary>
public class WebSocketErrorEventArgs : EventArgs
{
/// <summary>
/// 异常对象
/// </summary>
public Exception Exception { get; set; }
/// <summary>
/// 错误消息
/// </summary>
public string Message { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket消息事件参数
/// </summary>
public class WebSocketMessageEventArgs : EventArgs
{
/// <summary>
/// 消息类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 消息数据
/// </summary>
public object Data { get; set; }
/// <summary>
/// 原始二进制数据
/// </summary>
public byte[] RawData { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket消息失败事件参数
/// </summary>
public class WebSocketMessageFailedEventArgs : EventArgs
{
/// <summary>
/// 消息类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 消息数据
/// </summary>
public object Data { get; set; }
/// <summary>
/// 失败原因
/// </summary>
public string Reason { get; set; }
/// <summary>
/// 异常对象
/// </summary>
public Exception Exception { get; set; }
}
}

View File

@@ -0,0 +1,258 @@
using System;
using System.Collections.Generic;
using WebSocketSharp.Server;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket服务器实现
/// </summary>
public class WebSocketServer : IWebSocketServer
{
private WebSocketSharp.Server.WebSocketServer _server;
private Dictionary<string, WebSocketBehavior> _sessions;
// 事件
/// <summary>
/// 客户端连接事件
/// </summary>
public event EventHandler<WebSocketClientConnectedEventArgs> ClientConnected;
/// <summary>
/// 客户端断开事件
/// </summary>
public event EventHandler<WebSocketClientDisconnectedEventArgs> ClientDisconnected;
/// <summary>
/// 收到消息事件
/// </summary>
public event EventHandler<WebSocketMessageEventArgs> MessageReceived;
/// <summary>
/// 错误事件
/// </summary>
public event EventHandler<WebSocketErrorEventArgs> Error;
/// <summary>
/// 构造函数
/// </summary>
public WebSocketServer()
{
_sessions = new Dictionary<string, WebSocketBehavior>();
}
/// <summary>
/// 启动服务器
/// </summary>
/// <param name="port">端口号</param>
public void Start(int port)
{
try
{
_server = new WebSocketSharp.Server.WebSocketServer(port);
_server.AddWebSocketService<EasyWsBehavior>("/");
// 行为设置已在AddWebSocketService时完成
_server.Start();
}
catch (Exception ex)
{
Error?.Invoke(this, new WebSocketErrorEventArgs { Exception = ex, Message = ex.Message });
throw;
}
}
/// <summary>
/// 停止服务器
/// </summary>
public void Stop()
{
if (_server != null && _server.IsListening)
{
_server.Stop();
}
}
/// <summary>
/// 广播消息
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
public void Broadcast(string type, object data)
{
if (_server != null)
{
_server.WebSocketServices["/"].Sessions.Broadcast(Newtonsoft.Json.JsonConvert.SerializeObject(new { Type = type, Data = data }));
}
}
/// <summary>
/// 广播二进制消息
/// </summary>
/// <param name="data">二进制数据</param>
public void Broadcast(byte[] data)
{
if (_server != null)
{
_server.WebSocketServices["/"].Sessions.Broadcast(data);
}
}
/// <summary>
/// 向指定客户端发送消息
/// </summary>
/// <param name="clientId">客户端ID</param>
/// <param name="type">消息类型</param>
/// <param name="data">消息数据</param>
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);
}
}
/// <summary>
/// 向指定客户端发送二进制消息
/// </summary>
/// <param name="clientId">客户端ID</param>
/// <param name="data">二进制数据</param>
public void SendToClient(string clientId, byte[] data)
{
if (_server != null)
{
_server.WebSocketServices["/"].Sessions.SendTo(data, clientId);
}
}
/// <summary>
/// 检查服务器是否正在运行
/// </summary>
/// <returns>是否正在运行</returns>
public bool IsRunning()
{
return _server != null && _server.IsListening;
}
/// <summary>
/// 获取当前连接的客户端数量
/// </summary>
/// <returns>客户端数量</returns>
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);
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket状态改变事件参数
/// </summary>
public class WebSocketStatusChangedEventArgs : EventArgs
{
/// <summary>
/// 旧状态
/// </summary>
public string OldStatus { get; set; }
/// <summary>
/// 新状态
/// </summary>
public string NewStatus { get; set; }
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Threading;
namespace JoyD.Windows.CS.WebSocket
{
/// <summary>
/// WebSocket测试类
/// </summary>
public class WebSocketTest
{
/// <summary>
/// 测试WebSocket客户端
/// </summary>
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();
}
/// <summary>
/// 测试WebSocket服务器
/// </summary>
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();
}
}
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="13.0.4" targetFramework="net40" />
</packages>