在 WebSocket 聊天中广播

Broadcast in WebSocket chat

我想在 asp.net 核心中使用 WebSockets 聊天。我决定用中间件来做。然而有一件事我想了一天——我不知道如何进行正确的广播。 问题是它不显示其他用户的消息,而只是写你的消息用户数次。

这里应该是这样的(如果浏览器的 2 个选项卡中有 2 个用户):

1:你好2

2:你好 1

相反,我只看到 1 个用户的 2 条相同消息,而在浏览器的其他选项卡中我什么也看不到。

这是中间件class:

public class WebSocketChatMiddleware
{
    RequestDelegate _next;
    ConcurrentDictionary<string, WebSocket> connections { get; set; }


    public WebSocketChatMiddleware(RequestDelegate next)
    {
        _next = next;
        connections = new ConcurrentDictionary<string, WebSocket>();
    }


    public async Task InvokeAsync(HttpContext context)
    {
        if (!context.WebSockets.IsWebSocketRequest)
        {
            await _next.Invoke(context);
            return;
        }

        CancellationToken ct = context.RequestAborted;

        WebSocket ws = await context.WebSockets.AcceptWebSocketAsync();
        string wsID = Guid.NewGuid().ToString();

        connections.TryAdd(wsID, ws);



        while (true)
        {
            if (ct.IsCancellationRequested)
            {
                return;
            }
            string data = await ReadStringAsync(ws, ct);

            if (string.IsNullOrEmpty(data))
            {
                if (ws.State != WebSocketState.Open)
                {
                    break;
                }

                continue;
            }
            foreach (var item in connections) // not working broadcast
            {
                if (ws.State == WebSocketState.Open)
                {
                    await SendStringAsync(ws, data, ct);
                }
            }
        }
        WebSocket dummy;

        connections.TryRemove(wsID,out dummy);
        await ws.CloseOutputAsync(WebSocketCloseStatus.NormalClosure,"UserDisconnected",ct);

        ws.Dispose();


        await _next(context);
    }

   
    async Task<string> ReadStringAsync(WebSocket ws, CancellationToken ct = default)
    {
        var buffer = new ArraySegment<byte>(new byte[1024 * 8]);

        using (MemoryStream ms = new MemoryStream())
        {
            WebSocketReceiveResult receiveResult;

            do
            {
                ct.ThrowIfCancellationRequested();

                receiveResult = await ws.ReceiveAsync(buffer, ct); 

                ms.Write(buffer.Array, buffer.Offset, receiveResult.Count);

            } while (!receiveResult.EndOfMessage);


            ms.Seek(0, SeekOrigin.Begin); // Changing stream position to cover whole message


            if (receiveResult.MessageType != WebSocketMessageType.Text)
                return null;

            using (StreamReader reader = new StreamReader(ms, System.Text.Encoding.UTF8))
            {
                return await reader.ReadToEndAsync(); // decoding message
            }

        }
    }

    Task SendStringAsync(WebSocket ws, string data, CancellationToken ct = default)
    {
        var buffer = System.Text.Encoding.UTF8.GetBytes(data);
        var segment = new ArraySegment<byte>(buffer);
        return ws.SendAsync(segment, WebSocketMessageType.Text, true, ct);
    }

Startup.cs中的配置方法:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {

        app.UseWebSockets();

        app.UseMiddleware<WebSocketChatMiddleware>();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });
    }

非常感谢您的帮助 c:

更改如下代码:

          foreach (var item in connections) // not working broadcast
            {
                //if (ws.State == WebSocketState.Open)
                //{
                //    await SendStringAsync(ws, data, ct);
                //}
                if (item.Value.State != WebSocketState.Open)
                {
                    continue;
                }

                await SendStringAsync(item.Value, data, ct);
            }

结果: