ASP.NET Core WebSockets

Posted

技术标签:

【中文标题】ASP.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 从链接添加回显代码 【参考方案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);  
            
        

【讨论】:

以上是关于ASP.NET Core WebSockets的主要内容,如果未能解决你的问题,请参考以下文章

Asp.NET Core进阶 第四篇 Asp.Net Core Blazor框架

.NET Core 1.0ASP.NET Core 1.0和EF Core 1.0简介

asp.net core 注入后仍然报错?

深入研究 Mini ASP.NET Core(迷你 ASP.NET Core),看看 ASP.NET Core 内部到底是如何运行的

.Net Core 学习 - ASP.NET Core 概念学习

ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 14. ASP.NET Core Identity 入门