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.

815 lines
30 KiB

using System.Buffers;
using System.Net.WebSockets;
using System.Text;
using Microsoft.Extensions.Logging;
using CoreAgent.WebSocketTransport.Interfaces;
using CoreAgent.WebSocketTransport.Models;
using CoreAgent.WebSocketTransport.Middleware;
using System.Text.Json.Serialization;
using System.Text.Json;
namespace CoreAgent.WebSocketTransport.Services;
/// <summary>
/// WebSocket 传输实现
/// 单一职责:连接管理和自动数据流转
/// </summary>
public class WebSocketTransport : IWebSocketTransport
{
private readonly ILogger<WebSocketTransport> _logger;
private readonly IWebSocketConnection _connection;
private readonly IMessageSerializer _serializer;
private readonly IEnumerable<IMessageMiddleware> _middlewares;
private readonly WebSocketConfig _config;
private readonly IMessageChannelManager _channelManager;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly SemaphoreSlim _connectionSemaphore;
// 连接状态管理
private volatile bool _isConnected;
private DateTime? _lastHeartbeat;
private readonly object _heartbeatLock = new object();
private Task? _sendTask;
private Task? _receiveTask;
private Task? _heartbeatTask;
private Task? _reconnectTask;
private int _reconnectAttempts;
private readonly object _reconnectLock = new object();
public bool IsConnected => _isConnected;
public DateTime? LastHeartbeat
{
get
{
lock (_heartbeatLock)
{
return _lastHeartbeat;
}
}
}
public WebSocketTransport(
ILogger<WebSocketTransport> logger,
IWebSocketConnection connection,
IMessageSerializer serializer,
IEnumerable<IMessageMiddleware> middlewares,
WebSocketConfig config,
IMessageChannelManager channelManager)
{
_logger = logger;
_connection = connection;
_serializer = serializer;
_middlewares = middlewares;
_config = config;
_channelManager = channelManager;
_cancellationTokenSource = new CancellationTokenSource();
_connectionSemaphore = new SemaphoreSlim(1, 1);
}
/// <summary>
/// 异步连接 WebSocket 服务器
/// </summary>
public async Task ConnectAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("开始连接 WebSocket 服务器: {Url}", _config.Url);
await _connectionSemaphore.WaitAsync(cancellationToken);
try
{
if (_isConnected)
{
_logger.LogInformation("WebSocket 已连接,跳过重复连接");
return;
}
await ConnectInternalAsync(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "连接 WebSocket 服务器失败: {Url}", _config.Url);
throw;
}
finally
{
_connectionSemaphore.Release();
_logger.LogDebug("已释放连接信号量");
}
}
/// <summary>
/// 异步关闭连接
/// </summary>
public async Task CloseAsync(CancellationToken cancellationToken = default)
{
_logger.LogInformation("开始关闭 WebSocket 连接");
await _connectionSemaphore.WaitAsync(cancellationToken);
try
{
if (!_isConnected)
{
_logger.LogInformation("WebSocket 未连接,无需关闭");
return;
}
_logger.LogInformation("正在关闭 WebSocket 连接: {Url}", _config.Url);
// 停止后台任务
_cancellationTokenSource.Cancel();
_logger.LogDebug("已取消所有操作令牌");
// 等待任务完成
var closeTasks = new List<Task>();
if (_sendTask != null && !_sendTask.IsCompleted)
{
closeTasks.Add(_sendTask);
_logger.LogDebug("等待发送任务完成");
}
if (_receiveTask != null && !_receiveTask.IsCompleted)
{
closeTasks.Add(_receiveTask);
_logger.LogDebug("等待接收任务完成");
}
if (_heartbeatTask != null && !_heartbeatTask.IsCompleted)
{
closeTasks.Add(_heartbeatTask);
_logger.LogDebug("等待心跳任务完成");
}
if (_reconnectTask != null && !_reconnectTask.IsCompleted)
{
closeTasks.Add(_reconnectTask);
_logger.LogDebug("等待重连任务完成");
}
if (closeTasks.Count > 0)
{
_logger.LogInformation("等待 {TaskCount} 个后台任务完成", closeTasks.Count);
await Task.WhenAll(closeTasks);
_logger.LogInformation("所有后台任务已完成");
}
// 关闭连接
try
{
await _connection.CloseAsync(WebSocketCloseStatus.NormalClosure, "正常关闭", cancellationToken);
_logger.LogDebug("WebSocket 连接已正常关闭");
}
catch (Exception ex)
{
_logger.LogWarning(ex, "正常关闭连接失败,强制关闭连接");
_connection.ForceClose();
}
// 释放消息通道
try
{
_logger.LogDebug("开始释放消息通道");
_channelManager.ReleaseChannels();
_logger.LogInformation("消息通道释放成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "释放消息通道失败,但不影响连接关闭");
// 不抛出异常,确保不影响连接关闭流程
}
_isConnected = false;
_logger.LogInformation("WebSocket 连接关闭完成");
}
catch (Exception ex)
{
_logger.LogError(ex, "关闭 WebSocket 连接时发生异常");
throw;
}
finally
{
_connectionSemaphore.Release();
_logger.LogDebug("已释放连接信号量");
}
}
/// <summary>
/// 内部连接方法
/// </summary>
private async Task ConnectInternalAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("正在连接 WebSocket 服务器: {Url}, 超时时间: {TimeoutMs}ms", _config.Url, _config.TimeoutMs);
// 1. 先创建消息通道
CreateMessageChannels();
// 2. 建立 WebSocket 连接
await EstablishWebSocketConnectionAsync(cancellationToken);
// 3. 更新连接状态
_isConnected = true;
_reconnectAttempts = 0;
UpdateHeartbeat();
_logger.LogDebug("连接状态已更新,重连次数重置为 0");
// 4. 启动后台任务
StartBackgroundTasks();
_logger.LogInformation("WebSocket 连接成功建立,所有后台任务已启动");
}
/// <summary>
/// 创建消息通道
/// </summary>
private void CreateMessageChannels()
{
try
{
_logger.LogDebug("开始创建消息通道");
_channelManager.CreateChannels();
_logger.LogInformation("消息通道创建成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "创建消息通道失败");
throw;
}
}
/// <summary>
/// 建立 WebSocket 连接
/// </summary>
private async Task EstablishWebSocketConnectionAsync(CancellationToken cancellationToken)
{
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeoutCts.CancelAfter(_config.TimeoutMs);
try
{
await _connection.ConnectAsync(new Uri(_config.Url), timeoutCts.Token);
_logger.LogDebug("WebSocket 连接建立成功");
}
catch (Exception ex)
{
_logger.LogError(ex, "WebSocket 连接建立失败: {Url}", _config.Url);
// 连接失败时,确保状态正确
_isConnected = false;
_connection.ForceClose();
throw;
}
}
/// <summary>
/// 启动后台任务
/// </summary>
private void StartBackgroundTasks()
{
try
{
_logger.LogDebug("启动后台任务");
_sendTask = Task.Run(() => SendLoopAsync(_cancellationTokenSource.Token));
_receiveTask = Task.Run(() => ReceiveLoopAsync(_cancellationTokenSource.Token));
_heartbeatTask = Task.Run(() => HeartbeatLoopAsync(_cancellationTokenSource.Token));
_logger.LogDebug("后台任务启动完成: 发送={SendTaskId}, 接收={ReceiveTaskId}, 心跳={HeartbeatTaskId}",
_sendTask?.Id, _receiveTask?.Id, _heartbeatTask?.Id);
}
catch (Exception ex)
{
_logger.LogError(ex, "启动后台任务失败");
throw;
}
}
/// <summary>
/// 触发重连
/// </summary>
private void TriggerReconnect()
{
// 检查是否启用自动重连
if (!_config.EnableAutoReconnect)
{
_logger.LogInformation("自动重连功能已禁用,跳过重连操作");
return;
}
lock (_reconnectLock)
{
if (_reconnectTask != null && !_reconnectTask.IsCompleted)
{
_logger.LogDebug("重连任务已在运行,跳过重复触发");
return; // 重连任务已在运行
}
_logger.LogInformation("启动重连任务");
_reconnectTask = Task.Run(() => ReconnectLoopAsync(_cancellationTokenSource.Token));
_logger.LogDebug("重连任务已启动: {TaskId}", _reconnectTask.Id);
}
}
/// <summary>
/// 重连循环
/// </summary>
private async Task ReconnectLoopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("重连循环开始,当前重连次数: {Attempts}", _reconnectAttempts);
_isConnected = false;
while (_reconnectAttempts < _config.MaxReconnectAttempts && !cancellationToken.IsCancellationRequested)
{
_reconnectAttempts++;
var delaySeconds = Math.Min(Math.Pow(2, _reconnectAttempts - 1), 30);
var delay = TimeSpan.FromSeconds(delaySeconds);
_logger.LogWarning("WebSocket 连接断开,{DelaySeconds}秒后进行第{Attempt}次重连",
delaySeconds, _reconnectAttempts);
await Task.Delay(delay, cancellationToken);
try
{
_logger.LogInformation("开始第{Attempt}次重连尝试", _reconnectAttempts);
await ConnectInternalAsync(cancellationToken);
_logger.LogInformation("WebSocket 重连成功,重连次数: {Attempts}", _reconnectAttempts);
return;
}
catch (Exception ex)
{
_logger.LogError(ex, "WebSocket 重连失败,尝试次数: {Attempt}", _reconnectAttempts);
// 重连失败时,确保连接状态正确
_isConnected = false;
_connection.ForceClose();
// 重连失败时,释放消息通道
try
{
_logger.LogDebug("重连失败,释放消息通道");
_channelManager.ReleaseChannels();
}
catch (Exception channelEx)
{
_logger.LogError(channelEx, "重连失败时释放消息通道异常");
}
}
}
_logger.LogError("WebSocket 重连失败,已达到最大尝试次数: {MaxAttempts}", _config.MaxReconnectAttempts);
_isConnected = false;
}
/// <summary>
/// 心跳循环
/// </summary>
private async Task HeartbeatLoopAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
if (_isConnected && _connection.IsConnected)
{
// 检查发送通道是否有待发送的消息
var hasPendingMessages = _channelManager.SendChannel.Count > 0;
if (!hasPendingMessages)
{
_channelManager.PriorityChannel.TryWrite(new HeartbeatMessage());
UpdateHeartbeat();
_logger.LogTrace("发送心跳消息");
}
else
{
_logger.LogTrace("有待发送消息 ({MessageCount} 条),跳过心跳发送",
_channelManager.SendChannel.Count);
}
}
await Task.Delay(TimeSpan.FromSeconds(120), cancellationToken);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "心跳循环异常");
}
}
/// <summary>
/// 更新心跳时间
/// </summary>
private void UpdateHeartbeat()
{
lock (_heartbeatLock)
{
_lastHeartbeat = DateTime.UtcNow;
}
}
/// <summary>
/// 发送循环 - 自动从发送通道读取数据并发送
/// </summary>
private async Task SendLoopAsync(CancellationToken cancellationToken)
{
_logger.LogDebug("发送循环开始运行");
var messageCount = 0;
var priorityMessageCount = 0;
try
{
while (!cancellationToken.IsCancellationRequested && !_channelManager.SendChannel.IsCompleted)
{
// 优先处理优先级消息
while (_channelManager.PriorityChannel.TryRead(out var priorityMessage))
{
priorityMessageCount++;
if (priorityMessage is null) continue;
_logger.LogTrace("处理优先级消息 #{PriorityCount}: {MessageType}",
priorityMessageCount, priorityMessage?.GetType().Name ?? "null");
await SendSingleMessageAsync(priorityMessage, cancellationToken);
}
// 处理普通消息
if (_channelManager.SendChannel.TryRead(out var message))
{
messageCount++;
_logger.LogTrace("处理普通消息 #{Count}: {MessageType}",
messageCount, message?.GetType().Name ?? "null");
await SendSingleMessageAsync(message, cancellationToken);
}
else
{
// 如果没有消息,等待一小段时间
await Task.Delay(10, cancellationToken);
}
}
_logger.LogInformation("发送循环正常结束,共处理 {MessageCount} 条普通消息,{PriorityCount} 条优先级消息",
messageCount, priorityMessageCount);
}
catch (OperationCanceledException)
{
_logger.LogInformation("发送循环被取消,共处理 {MessageCount} 条普通消息,{PriorityCount} 条优先级消息",
messageCount, priorityMessageCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "发送循环异常,共处理 {MessageCount} 条普通消息,{PriorityCount} 条优先级消息",
messageCount, priorityMessageCount);
TriggerReconnect();
}
}
/// <summary>
/// 发送单个消息
/// </summary>
private async Task SendSingleMessageAsync(object message, CancellationToken cancellationToken)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var messageType = message?.GetType().Name ?? "null";
try
{
_logger.LogTrace("开始处理发送消息: {MessageType}", messageType);
// 通过中间件处理消息
var processedMessage = message;
var middlewareCount = 0;
foreach (var middleware in _middlewares)
{
middlewareCount++;
var middlewareName = middleware.GetType().Name;
_logger.LogTrace("通过中间件 {MiddlewareName} 处理消息", middlewareName);
processedMessage = await middleware.ProcessSendAsync(processedMessage, cancellationToken);
if (processedMessage == null)
{
_logger.LogDebug("消息被中间件 {MiddlewareName} 跳过", middlewareName);
return;
}
}
_logger.LogTrace("消息通过 {MiddlewareCount} 个中间件处理完成", middlewareCount);
if (processedMessage is byte[] binaryData)
{
// 处理二进制消息
_logger.LogTrace("发送二进制消息,大小: {Size} bytes", binaryData.Length);
await SendDataWithChunkingAsync(binaryData, WebSocketMessageType.Binary, cancellationToken);
}
else
{
// 处理文本消息
var data = _serializer.Serialize(processedMessage);
_logger.LogTrace("发送文本消息,大小: {Size} bytes", data.Length);
await SendDataWithChunkingAsync(data, WebSocketMessageType.Text, cancellationToken);
}
stopwatch.Stop();
_logger.LogDebug("消息发送成功: {MessageType}, 耗时: {ElapsedMs}ms", messageType, stopwatch.ElapsedMilliseconds);
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "发送消息失败: {MessageType}, 耗时: {ElapsedMs}ms", messageType, stopwatch.ElapsedMilliseconds);
throw;
}
}
/// <summary>
/// 分包发送数据
/// </summary>
private async Task SendDataWithChunkingAsync(byte[] data, WebSocketMessageType messageType, CancellationToken cancellationToken)
{
// 从配置中获取分包大小,如果没有配置则使用默认值
var maxChunkSize = _config.MaxChunkSize ?? 64 * 1024; // 默认64KB
if (data.Length <= maxChunkSize)
{
// 数据小于等于分包大小,直接发送
await _connection.SendAsync(new ArraySegment<byte>(data), messageType, true, cancellationToken);
_logger.LogTrace("数据大小 {Size} bytes 小于等于 {MaxChunkSize} bytes,直接发送", data.Length, maxChunkSize);
}
else
{
// 数据大于分包大小,需要分包发送
var totalChunks = (int)Math.Ceiling((double)data.Length / maxChunkSize);
_logger.LogInformation("数据大小 {Size} bytes 大于 {MaxChunkSize} bytes,将分 {TotalChunks} 个包发送",
data.Length, maxChunkSize, totalChunks);
var sentChunks = 0;
try
{
for (int i = 0; i < totalChunks; i++)
{
var offset = i * maxChunkSize;
var chunkSize = Math.Min(maxChunkSize, data.Length - offset);
var chunk = new ArraySegment<byte>(data, offset, chunkSize);
// 最后一个包设置 endOfMessage 为 true
var isLastChunk = i == totalChunks - 1;
_logger.LogTrace("发送第 {ChunkIndex}/{TotalChunks} 个包,大小: {ChunkSize} bytes,是否最后一个包: {IsLastChunk}",
i + 1, totalChunks, chunkSize, isLastChunk);
await _connection.SendAsync(chunk, messageType, isLastChunk, cancellationToken);
sentChunks++;
// 如果不是最后一个包,添加小延迟避免发送过快
if (!isLastChunk)
{
await Task.Delay(_config.ChunkDelayMs ?? 1, cancellationToken);
}
}
_logger.LogInformation("分包发送完成,共发送 {TotalChunks} 个包", totalChunks);
}
catch (Exception ex)
{
_logger.LogError(ex, "分包发送失败,已发送 {SentChunks}/{TotalChunks} 个包", sentChunks, totalChunks);
throw;
}
}
}
/// <summary>
/// 接收循环 - 自动接收数据并推送到接收通道
/// </summary>
private async Task ReceiveLoopAsync(CancellationToken cancellationToken)
{
_logger.LogDebug("接收循环开始运行");
var buffer = ArrayPool<byte>.Shared.Rent(4096);
var messageBuilder = new StringBuilder();
var binaryStream = new MemoryStream();
var messageCount = 0;
var binaryMessageCount = 0;
try
{
while (!cancellationToken.IsCancellationRequested &&
(_connection.State == WebSocketState.Open || _connection.State == WebSocketState.CloseReceived))
{
WebSocketReceiveResult result;
try
{
result = await _connection.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("WebSocket 未连接"))
{
_logger.LogWarning("WebSocket 连接状态异常,准备触发重连: {State}", _connection.State);
TriggerReconnect();
break;
}
_logger.LogTrace("接收到 WebSocket 消息: 类型={MessageType}, 大小={Count}, 结束={EndOfMessage}",
result.MessageType, result.Count, result.EndOfMessage);
if (result.MessageType == WebSocketMessageType.Text)
{
var text = Encoding.UTF8.GetString(buffer, 0, result.Count);
messageBuilder.Append(text);
if (result.EndOfMessage)
{
var message = messageBuilder.ToString();
messageCount++;
_logger.LogTrace("接收到完整文本消息 #{Count}: 长度={Length}", messageCount, message.Length);
await ProcessReceivedMessageAsync(message, cancellationToken);
messageBuilder.Clear();
}
}
else if (result.MessageType == WebSocketMessageType.Binary)
{
// 处理二进制消息
binaryStream.Write(buffer, 0, result.Count);
if (result.EndOfMessage)
{
var binaryData = binaryStream.ToArray();
binaryMessageCount++;
_logger.LogTrace("接收到完整二进制消息 #{Count}: 大小={Size} bytes", binaryMessageCount, binaryData.Length);
await ProcessReceivedMessageAsync(binaryData, cancellationToken);
binaryStream.SetLength(0);
}
}
else if (result.MessageType == WebSocketMessageType.Close)
{
_logger.LogInformation("收到 WebSocket 关闭消息,准备处理连接关闭");
// 收到关闭消息时,需要强制关闭连接并重新创建WebSocket实例
// 这样可以确保后续能够重新连接
_connection.ForceClose();
_isConnected = false;
_logger.LogInformation("WebSocket 连接已强制关闭,准备触发重连");
// 确保在触发重连之前,当前接收循环能够正常退出
// 重连任务会在后台启动,不会阻塞当前循环的退出
TriggerReconnect();
break;
}
}
_logger.LogInformation("接收循环正常结束,共接收 {TextCount} 条文本消息,{BinaryCount} 条二进制消息",
messageCount, binaryMessageCount);
}
catch (OperationCanceledException)
{
_logger.LogInformation("接收循环被取消,共接收 {TextCount} 条文本消息,{BinaryCount} 条二进制消息",
messageCount, binaryMessageCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "接收循环异常,共接收 {TextCount} 条文本消息,{BinaryCount} 条二进制消息",
messageCount, binaryMessageCount);
TriggerReconnect();
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
binaryStream.Dispose();
_logger.LogDebug("接收循环资源已清理");
}
}
/// <summary>
/// 处理接收到的消息
/// </summary>
private async Task ProcessReceivedMessageAsync(object message, CancellationToken cancellationToken)
{
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
var messageType = message?.GetType().Name ?? "null";
try
{
_logger.LogTrace("开始处理接收消息: {MessageType}", messageType);
// 通过中间件处理消息
var processedMessage = message;
var middlewareCount = 0;
foreach (var middleware in _middlewares)
{
middlewareCount++;
var middlewareName = middleware.GetType().Name;
_logger.LogTrace("通过中间件 {MiddlewareName} 处理接收消息", middlewareName);
processedMessage = await middleware.ProcessReceiveAsync(processedMessage, cancellationToken);
if (processedMessage == null)
{
_logger.LogDebug("接收消息被中间件 {MiddlewareName} 跳过", middlewareName);
return;
}
}
_logger.LogTrace("接收消息通过 {MiddlewareCount} 个中间件处理完成", middlewareCount);
// 推送到接收通道
if (processedMessage != null)
{
await _channelManager.ReceiveChannel.WriteAsync(processedMessage, cancellationToken);
_logger.LogTrace("接收消息已推送到接收通道");
}
else
{
_logger.LogTrace("接收消息为空,跳过推送到通道");
}
stopwatch.Stop();
_logger.LogDebug("接收消息处理完成: {MessageType}, 耗时: {ElapsedMs}ms", messageType, stopwatch.ElapsedMilliseconds);
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "处理接收消息异常: {MessageType}, 耗时: {ElapsedMs}ms", messageType, stopwatch.ElapsedMilliseconds);
}
}
private bool _disposed = false;
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_disposed) return;
_logger?.LogInformation("开始释放 WebSocket 传输资源");
try
{
// 取消所有操作
_cancellationTokenSource?.Cancel();
_logger?.LogDebug("已取消所有操作令牌");
// 等待信号量释放
if (_connectionSemaphore != null)
{
try
{
_connectionSemaphore.Wait(TimeSpan.FromSeconds(5));
_logger?.LogDebug("已获取连接信号量");
}
catch (TimeoutException)
{
_logger?.LogWarning("等待连接信号量超时");
}
}
// 收集需要等待的任务
var tasks = new List<Task>();
if (_sendTask != null && !_sendTask.IsCompleted)
{
tasks.Add(_sendTask);
_logger?.LogDebug("添加发送任务到等待列表");
}
if (_receiveTask != null && !_receiveTask.IsCompleted)
{
tasks.Add(_receiveTask);
_logger?.LogDebug("添加接收任务到等待列表");
}
if (_heartbeatTask != null && !_heartbeatTask.IsCompleted)
{
tasks.Add(_heartbeatTask);
_logger?.LogDebug("添加心跳任务到等待列表");
}
if (_reconnectTask != null && !_reconnectTask.IsCompleted)
{
tasks.Add(_reconnectTask);
_logger?.LogDebug("添加重连任务到等待列表");
}
// 等待所有任务完成
if (tasks.Count > 0)
{
_logger?.LogInformation("等待 {TaskCount} 个后台任务完成", tasks.Count);
var waitResult = Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(5));
if (waitResult)
{
_logger?.LogInformation("所有后台任务已成功完成");
}
else
{
_logger?.LogWarning("部分后台任务未在超时时间内完成");
}
}
else
{
_logger?.LogDebug("没有需要等待的后台任务");
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "释放资源过程中发生异常");
}
finally
{
try
{
// 释放托管资源
_cancellationTokenSource?.Dispose();
_connectionSemaphore?.Dispose();
_logger?.LogDebug("已释放托管资源");
}
catch (Exception ex)
{
_logger?.LogError(ex, "释放托管资源时发生异常");
}
_disposed = true;
GC.SuppressFinalize(this);
_logger?.LogInformation("WebSocket 传输资源释放完成");
}
}
}