在ASP.NET Core框架中,自定义WebSocket服务端中间件可以满足各类实时通信场景的需求,开发者可以通过编写中间件逻辑,灵活控制WebSocket连接的建立、消息处理和连接关闭等流程。

WebSocket中间件核心原理
ASP.NET Core的中间件本质是一个处理HTTP请求管道的委托,WebSocket中间件需要先判断当前请求是否为WebSocket请求,如果是则升级协议为WebSocket,之后就可以进行双向通信。核心流程分为三步:请求判断、协议升级、消息循环处理。
请求判断逻辑
WebSocket请求的HTTP头中会包含Connection: Upgrade和Upgrade: websocket字段,中间件需要先校验这些头信息,确认是合法的WebSocket请求后再进行后续处理。
协议升级操作
使用HttpContext.WebSockets.AcceptWebSocketAsync方法可以完成协议升级,升级成功后会返回WebSocket实例,后续所有消息收发都通过该实例完成。
完整实现代码
下面是一个可直接使用的自定义WebSocket中间件完整实现,包含连接管理、消息收发、异常处理等核心功能:
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using Microsoft.AspNetCore.Http;
namespace CustomWebSocketMiddleware
{
// WebSocket中间件类
public class CustomWebSocketMiddleware
{
private readonly RequestDelegate _next;
// 存储所有活跃的WebSocket连接,key为连接标识,value为WebSocket实例
private static readonly ConcurrentDictionary<string, WebSocket> _connections = new ConcurrentDictionary<string, WebSocket>();
public CustomWebSocketMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 判断是否为WebSocket请求
if (context.Request.Path == "/ws" && context.WebSockets.IsWebSocketRequest)
{
// 升级协议为WebSocket
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
// 生成唯一连接标识
string connectionId = Guid.NewGuid().ToString();
_connections.TryAdd(connectionId, webSocket);
try
{
// 处理消息循环
await HandleWebSocketAsync(webSocket, connectionId);
}
catch (Exception ex)
{
// 记录异常日志
Console.WriteLine($"WebSocket连接异常: {ex.Message}");
}
finally
{
// 移除连接
_connections.TryRemove(connectionId, out _);
// 关闭WebSocket连接
if (webSocket.State != WebSocketState.Closed)
{
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "连接关闭", CancellationToken.None);
}
}
}
else
{
// 不是WebSocket请求则交给下一个中间件处理
await _next(context);
}
}
// 处理WebSocket消息循环
private async Task HandleWebSocketAsync(WebSocket webSocket, string connectionId)
{
var buffer = new byte[1024 * 4];
WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
// 将接收到的字节转换为字符串
string receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
Console.WriteLine($"收到连接{connectionId}的消息: {receivedMessage}");
// 构造响应消息
string responseMessage = $"服务端已收到你的消息: {receivedMessage}";
byte[] responseBytes = Encoding.UTF8.GetBytes(responseMessage);
// 发送响应消息
await webSocket.SendAsync(new ArraySegment<byte>(responseBytes), result.MessageType, result.EndOfMessage, CancellationToken.None);
// 继续接收下一条消息
result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
}
// 处理连接关闭请求
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
// 向所有连接广播消息的静态方法
public static async Task BroadcastMessageAsync(string message)
{
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
foreach (var connection in _connections)
{
if (connection.Value.State == WebSocketState.Open)
{
await connection.Value.SendAsync(new ArraySegment<byte>(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None);
}
}
}
}
// 中间件扩展类,方便注册到管道
public static class CustomWebSocketMiddlewareExtensions
{
public static IApplicationBuilder UseCustomWebSocketMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CustomWebSocketMiddleware>();
}
}
}
中间件注册方式
实现完中间件后,需要在ASP.NET Core项目的Program.cs中注册该中间件,注册代码如下:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// 注册自定义WebSocket中间件,注意要放在路由中间件之前
app.UseCustomWebSocketMiddleware();
app.MapGet("/", () => "WebSocket服务端已启动,连接地址为 ws://ipipp.com:端口/ws");
app.Run();
功能扩展建议
上述基础实现可以满足简单的实时通信需求,实际项目中可以根据需要扩展以下功能:
- 增加连接鉴权逻辑,在协议升级前校验请求的身份信息,避免非法连接
- 完善消息格式定义,比如使用JSON格式封装消息类型、发送者、接收者等字段,实现点对点消息发送
- 增加连接心跳检测机制,定期发送心跳包,及时清理失效连接
- 结合依赖注入,将连接管理、消息处理等逻辑拆分为独立的服务,提升代码的可维护性
注意事项
开发WebSocket中间件时需要注意几个问题:首先是消息缓冲区的大小设置,需要根据实际消息长度调整,避免消息截断;其次是并发处理,多个连接同时发送消息时要保证线程安全,上述代码中使用ConcurrentDictionary存储连接就是为了避免并发问题;最后是异常处理,要捕获所有可能的异常,避免单个连接异常导致整个中间件崩溃。
C#_WebSocket自定义中间件WebSocket服务端ASP.NET_Core修改时间:2026-06-27 15:36:26