7

在我的应用程序中,我使用 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
}

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

  1. 我的 websocket 连接正确打开
  2. 从那里我的调试器进入await Task.WhenAll(OnMessage(webSocket));
  3. 之后我的调试器在行中等待var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
  4. 当从服务器发送消息时,我的调试将继续正确地得到结果。

我的问题和疑问是:

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

  2. 收到消息后,我将 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://stackoverflow.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

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

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

4

1 回答 1

3

关于您的最后一个问题,您可以从这篇文章中获得一些见解。

似乎 WebSocket 可以出于多种原因关闭,因此最好的方法是处理 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();
于 2017-11-07T08:18:52.547 回答