我正在尝试编写一个在.Net 8 中使用 System.Net.WebSockets 的程序,但我没有看到我期望的结果。这是一个不起作用的简化示例:
[ApiController]
[Route("[controller]")]
public class WebSocketController : ControllerBase
{
[HttpGet]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await ReceiveMessages(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
private static async Task ReceiveMessages(WebSocket webSocket)
{
WebSocketReceiveResult receiveResult;
var buffer = new byte[1];
do
{
receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
} while (!receiveResult.CloseStatus.HasValue);
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
}
客户端发送的第一条消息将被成功接收,然后
ReceiveAsync
将不会与客户端发送的任何其他消息一致地再次返回。
如果我添加另一行,在收到每条消息后将消息发送回客户端,则程序在接收客户端发送的每条消息时不会出现问题:
private static async Task ReceiveMessages(WebSocket webSocket)
{
WebSocketReceiveResult receiveResult;
var buffer = new byte[1];
do
{
receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
} while (!receiveResult.CloseStatus.HasValue);
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
在我的用例中,我不需要每次收到消息时都将消息发送回客户端。在查看了RFC和Microsoft的文档后,顶部代码块似乎应该按原样工作。我是否误解了有关 websockets 或 System.Net.WebSockets 的内容,或者这是 System.Net.WebSockets 的错误?
我最近也遇到了同样的问题。如果我错了,请纠正我,但就我而言,一旦 Get 请求完成,WebSocket 连接就会终止。
为了解决这个问题,我使用了 AutoResetEvent,尽管可能有更好的解决方案。然而,对于我的项目来说,这是迄今为止最好的解决方案。
在您的代码中,实现将如下所示:
[HttpGet]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
var stopHandle = new AutoResetEvent(false);
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await ReceiveMessages(webSocket, stopHandle);
stopHandle.WaitOne();
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
这个 stopHandle 现在在那里等待,直到被告知继续。
这正是您关闭连接时告诉它的内容。
private static async Task ReceiveMessages(WebSocket webSocket, AutoResetEvent stopHandle)
{
WebSocketReceiveResult receiveResult;
var buffer = new byte[1];
do
{
receiveResult = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None);
} while (!receiveResult.CloseStatus.HasValue);
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
stopHandle.Set(); // Tell the stopHandle not to wait any longer
}
正如我所说。它可能相对没有吸引力,但它确实有效。
顺便说一句。不要忘记将“UseWebSockets”中间件添加到请求管道中。
app.UseWebSockets();