|
@ -308,11 +308,19 @@ public class WebSocketMiddleware |
|
|
{ |
|
|
{ |
|
|
_logger.LogInformation("开始处理 WebSocket 消息,连接ID:{ConnectionId}", connectionId); |
|
|
_logger.LogInformation("开始处理 WebSocket 消息,连接ID:{ConnectionId}", connectionId); |
|
|
|
|
|
|
|
|
|
|
|
// 使用动态缓冲区大小,为客户端数据提供足够空间
|
|
|
|
|
|
// 缓冲区大小 = 业务限制 * 倍数,确保能处理客户端发送的大数据
|
|
|
|
|
|
var bufferSize = _options.GetActualBufferSize(); |
|
|
|
|
|
using var messageBuffer = new WebSocketMessageBuffer(bufferSize); |
|
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug("创建消息缓冲区,连接ID:{ConnectionId},缓冲区大小:{BufferSize}字节,业务限制:{MaxMessageSize}字节", |
|
|
|
|
|
connectionId, bufferSize, _options.MaxMessageSize); |
|
|
|
|
|
|
|
|
// 接收第一条消息
|
|
|
// 接收第一条消息
|
|
|
var receiveResult = await webSocket.ReceiveAsync( |
|
|
var receiveResult = await webSocket.ReceiveAsync( |
|
|
new ArraySegment<byte>(buffer), cancellationToken); |
|
|
new ArraySegment<byte>(buffer), cancellationToken); |
|
|
_logger.LogDebug("收到第一条消息,连接ID:{ConnectionId},消息类型:{MessageType}", |
|
|
_logger.LogDebug("收到第一条消息,连接ID:{ConnectionId},消息类型:{MessageType},结束标志:{EndOfMessage}", |
|
|
connectionId, receiveResult.MessageType); |
|
|
connectionId, receiveResult.MessageType, receiveResult.EndOfMessage); |
|
|
|
|
|
|
|
|
// 循环处理消息,直到收到关闭消息或发生错误
|
|
|
// 循环处理消息,直到收到关闭消息或发生错误
|
|
|
while (!receiveResult.CloseStatus.HasValue) |
|
|
while (!receiveResult.CloseStatus.HasValue) |
|
@ -338,11 +346,20 @@ public class WebSocketMiddleware |
|
|
// 处理有效消息类型(文本或二进制)
|
|
|
// 处理有效消息类型(文本或二进制)
|
|
|
if (IsValidMessageType(receiveResult.MessageType)) |
|
|
if (IsValidMessageType(receiveResult.MessageType)) |
|
|
{ |
|
|
{ |
|
|
_logger.LogDebug("处理消息,连接ID:{ConnectionId},消息类型:{MessageType},消息大小:{MessageSize}字节", |
|
|
_logger.LogDebug("处理消息片段,连接ID:{ConnectionId},消息类型:{MessageType},片段大小:{FragmentSize}字节,结束标志:{EndOfMessage}", |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count); |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count, receiveResult.EndOfMessage); |
|
|
await ProcessMessage(webSocket, connectionId, buffer, receiveResult, |
|
|
|
|
|
messageChannel, messageStartTime, cancellationToken); |
|
|
// 累积消息片段
|
|
|
|
|
|
await AccumulateMessageFragment(webSocket, connectionId, buffer, receiveResult, |
|
|
|
|
|
messageBuffer, messageChannel, messageStartTime, cancellationToken); |
|
|
|
|
|
|
|
|
|
|
|
// 如果消息完整,重置缓冲区并更新时间
|
|
|
|
|
|
if (receiveResult.EndOfMessage) |
|
|
|
|
|
{ |
|
|
|
|
|
messageBuffer.Reset(); |
|
|
messageStartTime = DateTime.UtcNow; |
|
|
messageStartTime = DateTime.UtcNow; |
|
|
|
|
|
_logger.LogDebug("消息完整接收,连接ID:{ConnectionId},重置缓冲区", connectionId); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
@ -353,35 +370,22 @@ public class WebSocketMiddleware |
|
|
// 接收下一条消息
|
|
|
// 接收下一条消息
|
|
|
receiveResult = await webSocket.ReceiveAsync( |
|
|
receiveResult = await webSocket.ReceiveAsync( |
|
|
new ArraySegment<byte>(buffer), cancellationToken); |
|
|
new ArraySegment<byte>(buffer), cancellationToken); |
|
|
_logger.LogDebug("接收下一条消息,连接ID:{ConnectionId},消息类型:{MessageType}", |
|
|
_logger.LogDebug("接收下一条消息,连接ID:{ConnectionId},消息类型:{MessageType},结束标志:{EndOfMessage}", |
|
|
connectionId, receiveResult.MessageType); |
|
|
connectionId, receiveResult.MessageType, receiveResult.EndOfMessage); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
_logger.LogInformation("WebSocket 消息处理完成,连接ID:{ConnectionId}", connectionId); |
|
|
_logger.LogInformation("WebSocket 消息处理完成,连接ID:{ConnectionId}", connectionId); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// 处理单个消息
|
|
|
/// 累积消息片段
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
/// <param name="webSocket">WebSocket 连接实例</param>
|
|
|
private async Task AccumulateMessageFragment( |
|
|
/// <param name="connectionId">连接ID</param>
|
|
|
|
|
|
/// <param name="buffer">接收缓冲区</param>
|
|
|
|
|
|
/// <param name="receiveResult">接收结果</param>
|
|
|
|
|
|
/// <param name="messageChannel">消息通道</param>
|
|
|
|
|
|
/// <param name="messageStartTime">消息开始时间</param>
|
|
|
|
|
|
/// <param name="cancellationToken">取消令牌</param>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// 该方法负责:
|
|
|
|
|
|
/// 1. 将接收到的数据写入消息缓冲区
|
|
|
|
|
|
/// 2. 检查消息是否完整
|
|
|
|
|
|
/// 3. 创建消息对象并写入通道
|
|
|
|
|
|
/// 4. 记录性能指标
|
|
|
|
|
|
/// </remarks>
|
|
|
|
|
|
private async Task ProcessMessage( |
|
|
|
|
|
System.Net.WebSockets.WebSocket webSocket, |
|
|
System.Net.WebSockets.WebSocket webSocket, |
|
|
string connectionId, |
|
|
string connectionId, |
|
|
byte[] buffer, |
|
|
byte[] buffer, |
|
|
WebSocketReceiveResult receiveResult, |
|
|
WebSocketReceiveResult receiveResult, |
|
|
|
|
|
WebSocketMessageBuffer messageBuffer, |
|
|
Channel<WebSocketMessage> messageChannel, |
|
|
Channel<WebSocketMessage> messageChannel, |
|
|
DateTime messageStartTime, |
|
|
DateTime messageStartTime, |
|
|
CancellationToken cancellationToken) |
|
|
CancellationToken cancellationToken) |
|
@ -392,9 +396,6 @@ public class WebSocketMiddleware |
|
|
{ |
|
|
{ |
|
|
var success = await _errorHandler.HandleWithRetryAsync(async () => |
|
|
var success = await _errorHandler.HandleWithRetryAsync(async () => |
|
|
{ |
|
|
{ |
|
|
// 为每个消息创建独立的消息缓冲区,避免并发问题
|
|
|
|
|
|
using var messageBuffer = new WebSocketMessageBuffer(_options.MaxMessageSize); |
|
|
|
|
|
|
|
|
|
|
|
// 验证输入参数
|
|
|
// 验证输入参数
|
|
|
if (buffer == null || buffer.Length == 0) |
|
|
if (buffer == null || buffer.Length == 0) |
|
|
{ |
|
|
{ |
|
@ -406,22 +407,47 @@ public class WebSocketMiddleware |
|
|
throw new WebSocketException($"无效的消息大小:{receiveResult.Count},缓冲区大小:{buffer.Length}"); |
|
|
throw new WebSocketException($"无效的消息大小:{receiveResult.Count},缓冲区大小:{buffer.Length}"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (receiveResult.Count > _options.MaxMessageSize) |
|
|
// 检查累积后的大小是否会超过缓冲区容量
|
|
|
|
|
|
// 这是技术层面的检查,防止缓冲区溢出
|
|
|
|
|
|
var totalSize = messageBuffer.Size + receiveResult.Count; |
|
|
|
|
|
if (totalSize > messageBuffer.MaxSize) |
|
|
{ |
|
|
{ |
|
|
throw new WebSocketException($"消息大小超过限制:{receiveResult.Count} > {_options.MaxMessageSize}"); |
|
|
throw new WebSocketException($"消息大小将超过缓冲区容量:{totalSize} > {messageBuffer.MaxSize}字节"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 累积消息片段
|
|
|
if (!messageBuffer.TryWrite(buffer, 0, receiveResult.Count)) |
|
|
if (!messageBuffer.TryWrite(buffer, 0, receiveResult.Count)) |
|
|
{ |
|
|
{ |
|
|
throw new WebSocketException("消息缓冲区溢出"); |
|
|
throw new WebSocketException("消息缓冲区写入失败"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug("消息片段已累积,连接ID:{ConnectionId},当前缓冲区大小:{BufferSize}字节,片段大小:{FragmentSize}字节", |
|
|
|
|
|
connectionId, messageBuffer.Size, receiveResult.Count); |
|
|
|
|
|
|
|
|
|
|
|
// 如果消息完整,创建完整的消息对象
|
|
|
if (receiveResult.EndOfMessage) |
|
|
if (receiveResult.EndOfMessage) |
|
|
{ |
|
|
{ |
|
|
|
|
|
var messageData = messageBuffer.GetMessage(); |
|
|
|
|
|
|
|
|
|
|
|
// 最终验证消息大小是否符合业务限制
|
|
|
|
|
|
// 这是业务层面的检查,超过限制会记录警告但继续处理
|
|
|
|
|
|
if (messageData.Length > _options.MaxMessageSize) |
|
|
|
|
|
{ |
|
|
|
|
|
_logger.LogWarning("消息大小超过业务限制,连接ID:{ConnectionId},大小:{Size}字节,限制:{MaxSize}字节,但已完整接收", |
|
|
|
|
|
connectionId, messageData.Length, _options.MaxMessageSize); |
|
|
|
|
|
|
|
|
|
|
|
// 可以选择:
|
|
|
|
|
|
// 1. 拒绝处理,返回错误
|
|
|
|
|
|
// 2. 记录警告但继续处理
|
|
|
|
|
|
// 3. 根据业务需求决定
|
|
|
|
|
|
|
|
|
|
|
|
// 这里选择记录警告但继续处理,确保数据完整性
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
var message = new WebSocketMessage |
|
|
var message = new WebSocketMessage |
|
|
{ |
|
|
{ |
|
|
ConnectionId = connectionId, |
|
|
ConnectionId = connectionId, |
|
|
Data = messageBuffer.GetMessage(), |
|
|
Data = messageData, |
|
|
MessageType = receiveResult.MessageType, |
|
|
MessageType = receiveResult.MessageType, |
|
|
IsComplete = true |
|
|
IsComplete = true |
|
|
}; |
|
|
}; |
|
@ -437,7 +463,8 @@ public class WebSocketMiddleware |
|
|
// 记录 Channel 消息处理统计
|
|
|
// 记录 Channel 消息处理统计
|
|
|
_channelManager.RecordMessageProcessed(messageChannel, message.Data.Length); |
|
|
_channelManager.RecordMessageProcessed(messageChannel, message.Data.Length); |
|
|
|
|
|
|
|
|
// messageBuffer 会在 using 语句中自动释放
|
|
|
_logger.LogDebug("完整消息已写入通道,连接ID:{ConnectionId},消息大小:{MessageSize}字节,处理时间:{ProcessingTime}ms", |
|
|
|
|
|
connectionId, message.Data.Length, processingTime); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
@ -448,20 +475,19 @@ public class WebSocketMiddleware |
|
|
} |
|
|
} |
|
|
catch (WebSocketException ex) |
|
|
catch (WebSocketException ex) |
|
|
{ |
|
|
{ |
|
|
_logger.LogError(ex, "WebSocket 消息处理异常,连接ID:{ConnectionId},消息类型:{MessageType},消息大小:{MessageSize}字节,错误:{ErrorMessage}", |
|
|
_logger.LogError(ex, "WebSocket 消息片段处理异常,连接ID:{ConnectionId},消息类型:{MessageType},片段大小:{FragmentSize}字节,错误:{ErrorMessage}", |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count, ex.Message); |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count, ex.Message); |
|
|
await HandleMessageProcessingFailure(webSocket, connectionId, cancellationToken); |
|
|
await HandleMessageProcessingFailure(webSocket, connectionId, cancellationToken); |
|
|
} |
|
|
} |
|
|
catch (OperationCanceledException ex) |
|
|
catch (OperationCanceledException ex) |
|
|
{ |
|
|
{ |
|
|
_logger.LogWarning(ex, "消息处理被取消,连接ID:{ConnectionId},消息类型:{MessageType},消息大小:{MessageSize}字节", |
|
|
_logger.LogWarning(ex, "消息片段处理被取消,连接ID:{ConnectionId},消息类型:{MessageType},片段大小:{FragmentSize}字节", |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count); |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count); |
|
|
// 取消操作转换为 WebSocket 异常
|
|
|
|
|
|
throw new WebSocketException("消息处理操作被取消"); |
|
|
throw new WebSocketException("消息处理操作被取消"); |
|
|
} |
|
|
} |
|
|
catch (Exception ex) |
|
|
catch (Exception ex) |
|
|
{ |
|
|
{ |
|
|
_logger.LogError(ex, "消息处理发生未知异常,连接ID:{ConnectionId},消息类型:{MessageType},消息大小:{MessageSize}字节,错误:{ErrorMessage}", |
|
|
_logger.LogError(ex, "消息片段处理发生未知异常,连接ID:{ConnectionId},消息类型:{MessageType},片段大小:{FragmentSize}字节,错误:{ErrorMessage}", |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count, ex.Message); |
|
|
connectionId, receiveResult.MessageType, receiveResult.Count, ex.Message); |
|
|
await HandleMessageProcessingFailure(webSocket, connectionId, cancellationToken); |
|
|
await HandleMessageProcessingFailure(webSocket, connectionId, cancellationToken); |
|
|
} |
|
|
} |
|
|