Asp.Net 作为客户端打开 websocket

Posted

技术标签:

【中文标题】Asp.Net 作为客户端打开 websocket【英文标题】:Asp.Net open websocket as a client 【发布时间】:2017-10-23 14:33:49 【问题描述】:

在我的应用程序中,我使用 SignalR 通过我的客户端进行消息传递。然而,该应用程序有一个到另一台服务器的 websocket 连接,用于通知如下:

var wsURL = (isSecure() ? "wss://" : "ws://") + wsEndpointURI + "?token=" + token + "&lang=" + language
         + "&uid=" + wsUid,
    notifierWs = new WebSocket(wsURL);

middlewareNotifierWs.onmessage = function (event) 
   var notification = JSON.parse(event.data),
       msg = notification.message;

       // Show the notification
;

我想做的是从我的 Asp.Net 应用程序建立这个连接,并通过将所有传入消息推送到正确的客户端来自己处理所有传入消息。 但是,我没有找到这种实现的示例。我找到的所有示例都是关于设置从我的服务器到客户端的 websocket 连接。 (我不得不提一下,我对异步函数不是很熟悉,所以可能我有一些与这个事实相关的错误)

更新

根据我发现的最好和(可能)最简单的方法是ClientWebSocket。

试图找到一些例子我发现了这个:Websocket - Server Using HttpListener and Client With ClientWebSocket

从这个例子看来,我可以创建一个到我想要的端点的 Websocket 连接。所以我就是这样做的

public static class Program

    private static UTF8Encoding encoding = new UTF8Encoding();

    public static async Task Connect()
    

        ClientWebSocket webSocket = null;
        try
        
            var url = serverWebSocketConnectionUrl;

            webSocket = new ClientWebSocket();
            await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
            await Task.WhenAll(OnMessage(webSocket));
        
        catch (Exception ex)
        
            // Log it
        
        finally
        
            if (webSocket != null)
            
                webSocket.Dispose();
            
        
    

    public static async Task OnMessage(ClientWebSocket webSocket)
    
        byte[] buffer = new byte[1024];
        while (webSocket.State == WebSocketState.Open)
        
            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Close)
            
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty,
                    CancellationToken.None);

            
            else
            
                WebSocketNotification notification = Newtonsoft.Json.JsonConvert.DeserializeObject<WebSocketNotification>(Encoding.UTF8.GetString(buffer));

                // Here I want to get to the signalR context and send my message to the correct user.
                var hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportingHub>();
                List<string> userIds = new List<string>();
                userIds.Add(notification.Id);
                hubContext.Clients.Users(userIds).OnMessage(notification);
            
        
    

在处理我插入的 signalR 方法的 javascript 文件中 应该从 signalR 触发的 onMessage 方法。 (就像我插入所有由 signalR 处理的方法一样,它们工作得很好。)

repHub.client.onMessage = function (notification) 
    // Show the message

根据我所做的,目前的结果是:

    我的 websocket 连接打开正确 从那里我的调试器进入await Task.WhenAll(OnMessage(webSocket));的onMessage 之后,我的调试器在行中等待var result = await webSocket.ReceiveAsync(new ArraySegment&lt;byte&gt;(buffer), CancellationToken.None); 从服务器发送消息后,我的调试继续正确并得到结果。

我的问题和疑问是:

    在某些时候触发了我的 Connect 方法的 finally,其中 websocket 被释放,我的监听器被释放。 (不再从await webSocket.ReceiveAsync 捕获任何发送的消息)。在什么情况下会发生这种情况?我应该寻找什么?

    收到消息后,我将 json 结果正确反序列化到 WebSocketNotification,但我的 javascript 函数 onMessage 永远不会触发。我错过了什么?

更新 2 关于第二个问题,我通过更改使其工作

repHub.client.onMessage => repHub.client.onmessage

我觉得这很奇怪,因为我使用 signalR 的报告中心在服务器端的所有方法都是首字母大写的 camelCase,而客户端的所有方法都是简单的 camelCase。为什么这个案例不同?我将不胜感激。

更新 3

我在我的 OnMessage 方法中插入了一个 try catch

    public static async Task OnMessage(ClientWebSocket webSocket)
    
        byte[] buffer = new byte[1024];
        while (webSocket.State == WebSocketState.Open)
        
            try
            
                // Receive data on ClientWebSocket as an asynchronous operation
                var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                if (result.MessageType == WebSocketMessageType.Close)
                
                    // If the server sends a message with message type for closure close the websocket connection
                    // Could use the CloseOutputAsync : https://***.com/questions/26744420/net-websocket-closeoutputasync-vs-closeasync
                    await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty,
                        CancellationToken.None);
                
                else
                

                    WebSocketNotification notification =
                        Newtonsoft.Json.JsonConvert.DeserializeObject<WebSocketNotification>(
                            Encoding.UTF8.GetString(buffer));
                    var hubContext = GlobalHost.ConnectionManager.GetHubContext<ReportingHub>();
                    List<string> userIds = new List<string>();
                    userIds.Add(notification.Id);
                    hubContext.Clients.Users(userIds).OnMessage(notification);

                
            
            catch (Exception ex)
            
                var a = ex;
            

        
    

在我的 Connect 进入 finally 并被处理之前,我得到了正确的错误,我得到了以下错误和堆栈跟踪

An internal WebSocket error occurred. Please see the innerException, if present, for more details.

at System.Net.WebSockets.WebSocketBase.ThrowIfConvertibleException(String methodName, Exception exception, CancellationToken cancellationToken, Boolean aborted)
at System.Net.WebSockets.WebSocketBase.<ReceiveAsyncCore>d__45.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
     at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)  
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at AthenaWeb.Infrastructure.NotificationWebSocket.<OnMessage>d__3.MoveNext() in C:\Branches\Notifications\trunk\AthenaWeb\Infrastructure\NotificationWebSocket.cs:line 69

我想我应该只是处理它并可能再次打开连接。 现在几乎这是我唯一的问题(如果没有其他要寻找的话):

这是最好的方法吗?我是否应该检查有关我的连接关闭原因的更多信息?

【问题讨论】:

如果这是您所要求的,则无法打开从服务器到客户端的 webSocket(除非客户端是服务器)。 @Pawel 我的实际情况是从我的前端 IIS 服务器打开一个 Web 套接字到我的中间件 WildFly 服务器。目前,已经从我的客户端的 javascript 到我的中间件 WildFly 服务器打开了一个 webcocket。我想使用这个 websocket 基础结构(来自中间件)从我的应用程序中打开它并自己处理用户通知。 【参考方案1】:

关于您的最后一个问题,您可以从post 获得一些见解。

WebSockets 似乎可以出于多种原因关闭,因此最好的方法是处理 WebSocket 关闭并可能重新打开它。

但是,从答案看来,您还应该检查 IIS 服务器的 I/O 完成端口。

@user4624881 提供的处理您的案例的一种方法是使用IAsyncResult.AsyncWaitHandle.WaitOne() 进行 websocket 中止处理。

像这样:

Stream s = this.GetStream();
IAsyncResult ar = s.BeginWrite(data, 0, data.Length, SendAsync, state);
if (!ar.IsCompleted)
    ar.AsyncWaitHandle.WaitOne();

【讨论】:

以上是关于Asp.Net 作为客户端打开 websocket的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net Mvc WebSocket - 客户端的自定义参数

关于ASP.NET Core WebSocket实现集群的思考

如何在 asp.net core 中使用 websockets

在 asp.net 核心服务器中处理关闭的 websocket 连接

在 Windows 7 上带有 websockets 的 asp.net 5 Web 应用程序

ASP.NET Core 使用 Web Socket