【问题标题】:ASP.NET Core WebSocketsASP.NET Core WebSockets
【发布时间】:2019-12-04 01:57:24
【问题描述】:

我正在尝试在 ASP.NET Core 上启动并运行 WebSocket 服务器。我创建了一个空的 web 项目 dotnet new webProgram.cs 更改为:

public static void Main(string[] args) {
    Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder => {
        webBuilder.UseStartup<Startup>();
    })
    .Build()
    .Run();
}

Startup.csConfigureServices 方法:

public void ConfigureServices(IServiceCollection services) {
    services.AddControllers();
    services.AddWebSockets();
}

Configure 方法:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    app.UseWebSockets();
    app.UseRouting();
    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
        endpoints.MapConnectionHandler<WebSocketHandler>("/ws");
    });
}

我的WebSocketHandlerOnConnectedAsync 方法如下:

public override async Task OnConnectedAsync(ConnectionContext connection) 
{
    var context = connection.GetHttpContext();
    var endpoint = $"{connection.RemoteEndPoint}";

    if (!context.WebSockets.IsWebSocketRequest) {
        connection.Abort();
        _logger.LogCritical($"Request from {endpoint} endpoint aborted.");
        return;
    }

    var websocket = await context.WebSockets.AcceptWebSocketAsync();
    _logger.LogInformation($"WebSocket request from {endpoint} endpoint accepted!");
}

当我尝试连接到APP_URL/ws 并且每次服务器在收到请求后立即关闭连接时,就会出现问题。以下是日志:https://pastebin.com/raw/34yu7thw

如果我在OnConnectedAsync 方法的末尾放置一个Task.Delay(-1),它会保持连接打开但会丢弃传入的连接。

我搜索了 MSDocs,但找不到太多关于如何使用 MapConnectionHandler&lt;T&gt; 的文档。

有一个while循环来接收来自OnConnectedAsync中多个客户端的消息对我来说安全吗? 这不是处理 websocket 连接的正确方法吗? MapConnectionHandler&lt;T&gt; 是瞬态的吗?

我真的很困惑,无法弄清楚它的行为。

【问题讨论】:

  • 这可能是你的问题。 "docs.microsoft.com/en-us/aspnet/core/fundamentals/…" 使用 WebSocket 时,必须在连接期间保持中间件管道运行。从您的日志中,它看起来好像在连接后立即关闭了 Web 套接字。一旦端点被接受,就可以看到它处理
  • 如果你把这个放在 await Echo(context, webSocket);接受网络套接字后。 add 从链接添加回显代码

标签: c# asp.net-core .net-core websocket


【解决方案1】:

我已经使用这些文档在 ASP.NET 核心中实现了一个 WebSocket 服务器,它对我来说效果很好:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1

主要思想是,在您接受带有AcceptWebSocketAsync() 的请求后,您获取返回的WebSocket 对象并使用它来发送和接收。通常,您会创建一个循环,调用ReceiveAsync 直到满足某些条件(当您收到特定消息时确定会话已完成,或者客户端断开连接等)。正如文档所述,When using a WebSocket, you must keep the middleware pipeline running for the duration of the connection. 因此,如果您将 WebSocket 连接传递给后台工作人员以执行发送/接收,您需要在与该客户端交互期间保持该管道打开。然后,当您完成后,您会向中间件发出信号,表明连接已完成并且可以展开。

我怀疑不保持连接打开和循环是您的问题。我之前没有在 WebSockets 上使用过 MapConnectionHandler,所以这可能行不通,但上述策略可能会对您有所帮助,或者像这样使用后台工作人员关注文档:

app.Use(async (context, next) => {
    var socket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(socket, socketFinishedTcs); 

    await socketFinishedTcs.Task;
});

【讨论】:

  • 嘿,我试过循环等等,似乎MapConnectionHandler 不适合它。构建一个合适的中间件为我解决了这个问题。
  • 我得到未定义的BackgroundSocketProcessor?我错过了什么图书馆?
  • 如何将套接字实际传递给后台工作人员,BackgroundSocketProcessor.AddSocket 中的内容是什么?
【解决方案2】:

所以,我完成了我的目标。完整的用例在这里:https://github.com/Yucked/Rhapsody/blob/beta/src/Controllers/WebSocketHandler.cs

此方法使用System.IO.Pipelines。我没有旧的源代码,因为我废弃了它,甚至在保持管道打开之后也无法弄清楚为什么连接被丢弃,但是现在它可以工作了!

            app.UseEndpoints(endpoints => {
                endpoints.MapControllers();
                endpoints.MapConnectionHandler<WebSocketHandler>("/player/{playerId}");
            });
        public override async Task OnConnectedAsync(ConnectionContext connection) {
            var httpContext = connection.GetHttpContext();

            if (!httpContext.WebSockets.IsWebSocketRequest) {
                await httpContext.Response.CompleteAsync();
                return;
            }

            await httpContext.WebSockets.AcceptWebSocketAsync()
               .ContinueWith(async task => {
                    var webSocket = await task;
                    await HandleConnectionAsync(webSocket);
                });
        }

        private async Task HandleConnectionAsync(WebSocket websocket) {
            try {
                do {
                    var memory = writer.GetMemory(BUFFER_SIZE);
                    var receiveResult = await webSocket.ReceiveAsync(memory, CancellationToken.None);
                    if (!receiveResult.EndOfMessage) {
                        writer.Advance(receiveResult.Count);
                        continue;
                    }

                    await writer.FlushAsync();
                } while (webSocket.State == WebSocketState.Open);
            }
            catch (Exception exception) {
                await writer.CompleteAsync(exception);  
            }
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    • 1970-01-01
    • 2018-09-11
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多