WebAPI-WebSocket

WebAPI-WebSocket

HTTP是短链接

WebSocket是长连接

HTTP通勤是单向的,基于请求响应模式

WebSocket支持双向通信

HTTP和WebSocket底层都是TCP链接


处理请求

在ASP.NETCore中,我们甚至可以直接使用控制器来处理WS请求,但这并不符合设计规范,所以我们使用第二种方法——中间件处理Ws请求

// 使用 WebSocket 中间件
app.UseWebSockets();

app.MapControllers(); // 映射控制器


// 处理 WebSocket 请求
// 定义一个路由 `/ws`,并处理 WebSocket 请求
app.Map("/ws", async context =>
{
    // 检查当前请求是否是 WebSocket 请求
    if (context.WebSockets.IsWebSocketRequest)
    {
        // 接受 WebSocket 连接
        using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
        
        // 处理 WebSocket 连接
        await HandleWebSocketConnection(webSocket);
    }
    else
    {
        // 如果请求不是 WebSocket 请求,返回 400 Bad Request 状态码
        context.Response.StatusCode = 400;
    }
});

app.Run();

// 处理 WebSocket 连接的方法
async Task HandleWebSocketConnection(WebSocket webSocket)
{
    // 创建一个缓冲区来接收消息
    var buffer = new byte[1024 * 4];
    
    // 只要 WebSocket 连接处于打开状态,就继续接收消息
    while (webSocket.State == WebSocketState.Open)
    {
        // 接收消息
        var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        
        // 如果收到的消息是文本类型
        if (result.MessageType == WebSocketMessageType.Text)
        {
            // 将字节转换为字符串
            var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
            
            // 打印收到的消息
            Console.WriteLine($"Received: {message}");
            
            // 将收到的消息发送回客户端
            await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
        }
        // 如果收到的消息是关闭连接的类型
        else if (result.MessageType == WebSocketMessageType.Close)
        {
            // 关闭 WebSocket 连接
            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
        }
    }
}

广播消息

在项目中,如果有将客户端发来的消息推送给服务端,广播方式也是一种很好的选择

Program.cs
// 开启ws中间件支持
app.UseWebSockets();

app.MapControllers();

#region ws

// 使用中间件管道过滤ws请求
app.Map("/ws/{sid}", async (context) =>
{
    // 如果为ws请求
    if (context.WebSockets.IsWebSocketRequest)
    {
        // 从路径中获取客户端id
        var sid = context.Request.RouteValues["sid"] as string;
        
        // 从容器中获取到WebSocketService 工具类
        var webSocketService = context.RequestServices.GetRequiredService<WebSocketService>();
        
        // 允许与客户端建立ws链接
        using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
        
        // 调用工具类中的HandleWebSocketConnection执行ws请求的处理逻辑
        await webSocketService.HandleWebSocketConnection(webSocket, sid, context.RequestAborted);
    }
    else
    {
        // 返回请求失败
        context.Response.StatusCode = 400; // Bad Request
    }
});

#endregion

app.Run();
webSocketService工具类
public class WebSocketService
{
    /// <summary>
    /// 存放会话对象
    /// </summary>
    private static ConcurrentDictionary<string, WebSocket> sessionMap = new ConcurrentDictionary<string, WebSocket>();

    /// <summary>
    /// 连接建立成功调用的方法
    /// </summary>
    /// <param name="webSocket"></param>
    /// <param name="sid"></param>
    public async Task OnOpen(WebSocket webSocket, string sid)
    {
        Console.WriteLine($"客户端:{sid} 建立连接");
        sessionMap.TryAdd(sid, webSocket); // 将客户端和请求保存到集合中
    }

    /// <summary>
    /// 收到客户端消息后调用的方法
    /// </summary>
    /// <param name="webSocket"></param>
    /// <param name="message"></param>
    /// <param name="sid"></param>
    public async Task OnMessage(WebSocket webSocket, string message, string sid)
    {
        Console.WriteLine($"收到来自客户端:{sid} 的信息: {message}");
    }

    /// <summary>
    /// 连接关闭调用的方法
    /// </summary>
    /// <param name="sid"></param>
    public void OnClose(string sid)
    {
        // 如果成功的将客户端从集合中移除
        if (sessionMap.TryRemove(sid, out _))
        {
            Console.WriteLine($"连接断开: {sid}");
        }
    }

    /// <summary>
    /// 群发消息
    /// </summary>
    /// <param name="message"></param>
    public async Task SendToAllClients(string message)
    {
        var buffer = Encoding.UTF8.GetBytes(message);
        foreach (var webSocket in sessionMap.Values)
        {
            if (webSocket.State == WebSocketState.Open)
            {
                await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true,
                    CancellationToken.None);
            }
        }
    }


    /// <summary>
    /// 处理 WebSocket 连接
    /// </summary>
    /// <param name="webSocket"></param>
    /// <param name="sid"></param>
    /// <param name="cancellationToken"></param>
    public async Task HandleWebSocketConnection(WebSocket webSocket, string sid, CancellationToken cancellationToken)
    {
        await OnOpen(webSocket, sid);

        var buffer = new byte[1024 * 4];
        while (webSocket.State == WebSocketState.Open)
        {
            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
            if (result.MessageType == WebSocketMessageType.Text)
            {
                var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                await OnMessage(webSocket, message, sid);
            }
            else if (result.MessageType == WebSocketMessageType.Close)
            {
                OnClose(sid);
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cancellationToken);
            }
        }
    }