.Net 简单实现WebSocket通讯

使用框架:.net 8、winform

操作系统:Windows 11

编译器:vs 2022

内容:实现一个多客户端单服务端的WebSocket通讯,并可发布广播消息

本文使用.net框架自带的WebSocket WebSocketClient服务类进行演示,实现一个简单的通讯。

服务端

服务端所要做的事情就是创建一个WebSocket服务器,并在有请求时进行响应。由于我们是要实现一个多客户端可广播的的通讯,而每个 WebSocket 连接都是独立处理的,若要实现广播,则需要维护一个连接列表,并在接收到消息时遍历这个列表来发送消息。

Storage类


public interface IWebSocketStorage
{
    public ConcurrentDictionary<string, List<WebSocketObj>> WebSocketStorageList { get; }
    Task<WebSocket> GetWebSocket(string UserId);
    WebSocketObj AddWebSocketClient(string userId, WebSocket ws, string Name);
    void DisconnectWebSocket(string userId, WebSocketObj ws);
}

public class WebSocketStorage : IWebSocketStorage
{
    public WebSocketStorage()
    {
        WebSocketStorageList = new ConcurrentDictionary<string, List<WebSocketObj>>();
    }


    //WebSocket链接列表
    public ConcurrentDictionary<string, List<WebSocketObj>> WebSocketStorageList { get; }


    //将链接添加至列表
    public WebSocketObj AddWebSocketClient(string userId, WebSocket ws, string Name)
    {
        var wsObj = new WebSocketObj()
        {
            
            WebSocket = ws,
            ConnectionGuid = Guid.NewGuid(),
            Name = Name,
        };
        if (WebSocketStorageList.ContainsKey(userId))
        {
            WebSocketStorageList[userId].Add(wsObj);
        }
        else
        {
            WebSocketStorageList.TryAdd(userId, new List<WebSocketObj>() { wsObj });
        }

        return wsObj;
    }

    //断开链接
    public void DisconnectWebSocket(string userId, WebSocketObj ws)
    {
        if (WebSocketStorageList.TryGetValue(userId, out List<WebSocketObj> value))
        {
            value.Remove(ws);
        }
    }


    //获取WebSocket链接
    public async Task<WebSocket> GetWebSocket(string userId)
    {

        if (WebSocketStorageList.TryGetValue(userId, out List<WebSocketObj> value))
        {
            return value.FirstOrDefault().WebSocket;
        }

        return null;
    }
    
}

public class WebSocketObj
{
    [Newtonsoft.Json.JsonIgnore]
    public WebSocket WebSocket { get; set; }
    public Guid ConnectionGuid { get; set; }
    public string Name { get; set; }
}

中间件

public class WebsocketHandlerMiddleware : IMiddleware
{
    private readonly IWebSocketStorage _webSocketStorage;
    private readonly PathString wsPath = new PathString("/ws");

    public WebsocketHandlerMiddleware(IWebSocketStorage webSocketStorage)
    {
        _webSocketStorage = webSocketStorage;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        if (context.Request.Path.Equals(wsPath))
        {
            if (context.WebSockets.IsWebSocketRequest)
            {
                string name = "";
                string userId = "";    //客户端传来

                using WebSocket socket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
                WebSocketObj clientObj1 = new WebSocketObj();
                clientObj1 = _webSocketStorage.AddWebSocketClient(userInfo.UserId, socket, name);
                try
                {
                    while (socket.State != WebSocketState.Closed && socket.State != WebSocketState.Aborted)
                    {
                        byte[] buffer = new byte[1024 * 1];
                        WebSocketReceiveResult receiveResult = await socket.ReceiveAsync(buffer, context.RequestAborted).ConfigureAwait(false);

                        if (socket.State == WebSocketState.CloseReceived && receiveResult.MessageType == WebSocketMessageType.Close)
                        {
                            await socket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Acknowledge Close frame", context.RequestAborted).ConfigureAwait(false);
                        }

                        if (socket.State == WebSocketState.Open && receiveResult.MessageType == WebSocketMessageType.Text)
                        {
                            string msgStr = Encoding.UTF8.GetString(buffer).Trim();    //客户端发来的消息
                            byte[] msgBytes = Encoding.UTF8.GetBytes("消息");
                            socket.SendAsync(new ArraySegment<byte>(msgBytes), WebSocketMessageType.Text, true, CancellationToken.None);    //向客户端发送消息
                            var wsMessage = await _webSocketStorage.GetWebSocket("1", WebSocketType.Message);
                            }
                            
                    }
                }
                catch (Exception)
                {
                    //ignore
                }
                finally
                {
                    _webSocketStorage.DisconnectWebSocket(userInfo.UserId, clientObj1);
                    
                }
            }
            else
            {
                context.Response.StatusCode = 404;
            }
        }
        else
        {
            await next(context);
        }
    }
}

IOC

builder.Services.AddSingleton<IWebSocketStorage, WebSocketStorage>();
builder.Services.AddScoped<WebsocketHandlerMiddleware>();

如果想要广播消息,那么在该类库加上var wsStorage = App.GetService<IWebSocketStorage>();,遍历其中的WebSocketStorageList然后发消息即可。

客户端

客户端只需要连接WebSocket服务器,然后收发消息即可

public class Ws
{
    private ClientWebSocket webSocket = new ClientWebSocket();
    readonly ChromiumWebBrowser _browser;

    public async void ConnectToWebSocketServer()
    {
        try
        {
            Uri serverUri = new Uri("ws://你的WebSocket地址");
            await webSocket.ConnectAsync(serverUri, CancellationToken.None);

            await SendMessageAsync(userId);
            await ReceiveMessages();
        }
        catch (Exception ex)
        {
            MessageBox.Show($"无法连接到WebSocket服务器: {ex.Message}");
        }

    }

    public async Task SendMessageAsync(string message)
    {
        if (webSocket?.State == WebSocketState.Open)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(message);
            ArraySegment<byte> segment = new ArraySegment<byte>(bytes);

            await webSocket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None);
        }
    }

    public async Task ReceiveMessages()
    {
        var buffer = new byte[1024 * 4];
        string message = string.Empty;
        while (webSocket.State == WebSocketState.Open)
        {
            buffer = new byte[1024 * 4];
            WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Text)
            {
                message = Encoding.UTF8.GetString(buffer, 0, result.Count).Trim();
            }
            else if (result.MessageType == WebSocketMessageType.Close)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
                break;
            }
        }
    }

   public async Task Close()
    {
        if (webSocket != null && webSocket.State == WebSocketState.Open)
        {
            await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
        }
    }


}

总结

对于需要实时数据更新的场景,WebSocket通常是一个很好的选择。

当然,如果不考虑实时以及负载的话,长轮询其实也可以用来模拟长连接,具体情况具体分析吧大伙🤯

posted @ 2024-08-08 18:13  有了一个点子  阅读(168)  评论(0编辑  收藏  举报