我在ASP.NET Core 2.0中处理一些websockets。我想接受传入的请求并将它们移交给单一服务,以便可以从API控制器轻松地将消息发送给它们。我的服务器根本没有读取任何东西,它只接受连接并向它们发送消息。
每当我尝试访问套接字以发送消息时,它总是被丢弃(ObjectDisposedException
)。我不确定如何以不处理它的方式将连接从中间件正确传递到服务。我怎么能这样做?
websocket manager(singleton):
public class WebSocketManager
{
private readonly ConcurrentDictionary<Guid, WebSocket> _sockets;
protected WebSocketManager()
{
_sockets = new ConcurrentDictionary<Guid, WebSocket>();
}
public Guid AddWebSocket(WebSocket socket)
{
var guid = Guid.NewGuid();
_sockets.TryAdd(guid, socket);
return guid;
}
public async Task SendAsync(Guid guid, string message)
{
if (!_sockets.TryGetValue(guid, out var socket))
return;
await SendMessageAsync(socket, message);
}
public async Task SendAllAsync(string message)
{
foreach (var socket in _sockets)
{
if (socket.Value.State == WebSocketState.Open)
await SendMessageAsync(socket.Value, message);
else
await RemoveWebSocketAsync(socket.Key);
}
}
private async Task SendMessageAsync(WebSocket socket, string message)
{
await socket.SendAsync(
new ArraySegment<byte>(Encoding.UTF8.GetBytes(message)),
WebSocketMessageType.Text,
true,
CancellationToken.None);
}
private async Task RemoveWebSocketAsync(Guid guid)
{
if (!_sockets.TryRemove(guid, out var socket))
return;
if (socket?.State == WebSocketState.Open)
await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
socket.Dispose();
}
}
接受连接的中间件:
public class WebSocketMiddleware
{
private readonly RequestDelegate _next;
private readonly WebSocketManager _wsMgr;
public WebSocketMiddleware(
RequestDelegate next,
WebSocketManager wsMgr)
{
_next = next;
_wsMgr = wsMgr;
}
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path != "/ws")
{
await _next(context);
return;
}
if (!context.WebSockets.IsWebSocketRequest)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("This endpoint accepts only websocket connections.");
return;
}
var guid = _wsMgr.AddWebSocket(await context.WebSockets.AcceptWebSocketAsync());
// this call succeeds
await _wsMgr.SendAsync(guid, "test message");
}
}
控制器调用示例:
public class WinLossController : Controller
{
private readonly WebSocketManager _wsMgr;
public WinLossController(WebSocketManager wsMgr)
{
_wsMgr = wsMgr;
}
[HttpPost]
public async Task<IActionResult> Update()
{
await _wsMgr.SendAllAsync("test controller update");
return NoContent();
}
}
答案 0 :(得分:5)
一旦离开InvokeAsync
中间件 - websocket连接关闭,这就是预期的流量。
正确的方法是阻止侦听套接字上的传入消息。即使您不希望客户端有任何数据 - 您仍然需要这样做,因为客户端可能向您发送的消息包括一些控制消息,例如乒乓消息(heartbeat)和“close”消息,表明客户端是关闭沟通。
所以做完之后:
var guid = _wsMgr.AddWebSocket(await context.WebSockets.AcceptWebSocketAsync());
在你的经理中实现一个循环,至少处理“关闭”消息并等待:
await _wsMgs.ReceiveAsync(guid);
如果您需要在连接后立即向客户端发送内容 - 在等待接收循环之前执行此操作,因为此循环应仅在您完成此websocket时完成。