在 MVC 控制器中打开 websocket 通道

Posted

技术标签:

【中文标题】在 MVC 控制器中打开 websocket 通道【英文标题】:Opening a websocket channel inside MVC controller 【发布时间】:2016-02-22 12:48:00 【问题描述】:

有没有人在 MVC 控制器中打开 websocket 连接有什么好的经验?

技术栈: ASPNET Core 1.0 (RC1) MVC、dnx46、System.Net.WebSockets

为什么使用 MVC 而不是中间件:为了整体一致性、路由、已注入的存储库、在同一控制器中调用私有方法的选项。

[HttpGet("v1/resources/id")]
public async Task<IActionResult> GetAsync(string id)

    var resource = await this.repository.GetAsync(id);
    if (resource == null)
    
        return new HttpStatusCodeResult(404);
    

    if (this.HttpContext.WebSockets.IsWebSocketRequest)
    
        var webSocket = await this.HttpContext.WebSockets.AcceptWebSocketAsync();
        if (webSocket != null && webSocket.State == WebSocketState.Open)
        
            while (true)
            
                var response = string.Format("Hello! Time 0", System.DateTime.Now.ToString());
                var bytes = System.Text.Encoding.UTF8.GetBytes(response);

                await webSocket.SendAsync(new System.ArraySegment<byte>(bytes),
                    WebSocketMessageType.Text, true, CancellationToken.None);

                await Task.Delay(2000);
            
        
    

    return new HttpStatusCodeResult(101);           

问题:在中间件中处理 websocket 连接是否有任何已知的缺点?握手呢,除了返回 HTTP 101 状态码,我们还需要做什么吗?

更新 1:为什么不使用 SignalR? 不需要使用后备技术,因此虽然它是一款不错的产品,但在这种情况下添加额外的依赖项并没有什么好处。

更新 2: 我已经注意到一个缺点 - 当 while(true) 存在时(为了简单起见,上面的示例中没有显示,比如说,当频道需要关闭),方法需要返回一些东西(任务)。它应该是什么? HTTP 200 状态响应?我猜不是,因为在 WebSockets 文档中写到,在“关闭”帧之后什么都不应该发送。

更新 3: 我学到了一件事,那就是如果你想在 Windows 10 上使用 IIS Express 10.0 在 Visual Studio 2015 中调试时让 WebSockets 工作,你仍然必须使用 @987654321 @ 并在 Startup.cs 文件中配置 app.UseWebSockets()。否则,IsWebSocketRequest 将为 false。有谁知道为什么?握手?

【问题讨论】:

使用 SignalR asp.net/signalr 我已经成功地将 websocketsharp 与 MVC6 / MVC Core 1.0 / 本周他们称之为的任何东西混合在一起。事情不通过 OWIN 中间件管道,但您可以在不同的端口上运行 Web 套接字服务器并让它为 WS 连接提供服务。只是思考的食物。 未验证,但我怀疑“最正确”的方法是定义您自己的 IActionResult 自定义实现,并在其 ExecuteResultAsync 方法中使用 websocket 循环。然后控制器只返回一个实例。 我只是这样做了——我将 websocket 处理循环放在自定义 IActionResult 类的 ExecuteResultAsync 中。这避免了其他中间件尝试在 OnStartup 中设置内容但已发送标头的问题。 能否请您与我们分享您的最终实现,了解您是如何在 asp.net MVC 5 的操作方法中运行 WebSocket 的? 【参考方案1】:

看起来不错。

您可能希望将while(true) 更改为while (!HttpContext.RequestAborted.IsCancellationRequested),以便检测客户端断开连接并结束请求。 调用accept 后无需检查null 或websocket 的状态。

我假设所有这些代码都是临时的,您实际上会从 websocket 读取一些内容。

所有常用的 websocket 规则都适用:

使用 SSL(当您实际托管时) 它不适用于多台服务器(它是点对点套接字连接) 您需要支持处理部分帧。如果您知道客户不会发送任何内容,则可以取消此操作。

【讨论】:

谢谢,很棒的提示!我不会从服务器端的套接字读取数据。

以上是关于在 MVC 控制器中打开 websocket 通道的主要内容,如果未能解决你的问题,请参考以下文章

如何在 websocket onopen 事件中访问 spring mvc 属性值?

Websockets 控制用户访问

C# mvc统一通道使用过滤器

如何在 Rails 控制器中调用通道方法?

在 Redux 中存储 websocket(通道)连接对象

在Angular 2+客户端和Django后端之间打开WebSocket连接