在 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);
}
结果:
我想在 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);
}
结果: