Asp.Net открыть веб-сокет в качестве клиента

В своем приложении я использую SignalR для обмена сообщениями через своих клиентов. Приложение, однако, имеет соединение веб-сокета с другим сервером для таких уведомлений:

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 и обработать все входящие сообщения самостоятельно, отправив их нужному клиенту. Я не нахожу, однако, пример такой реализации. Все примеры, которые я нахожу, касаются установки соединения через веб-сокет от моего сервера к клиенту. (Я должен отметить, что я не очень знаком с асинхронными функциями, поэтому, возможно, у меня есть некоторые ошибки, связанные с этим фактом)

Обновить

Из того, что я нашел, лучший и (возможно) самый простой способ - это ClientWebSocket.

Пытаясь найти несколько примеров, я нашел это: Websocket - сервер с использованием HttpListener и клиент с 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);
            }
        }
    }
}

Внутри моего javascript-файла, обрабатывающего методы signalR, я вставил метод onMessage, который должен запускаться из signalR. (Таким же образом я вставил все методы, обработанные signalR, и они работают просто отлично.)

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

Из того, что я сделал, текущие результаты:

  1. Мое подключение к веб-сокету открывается правильно
  2. Оттуда мой отладчик попадает в сообщение await Task.WhenAll(OnMessage(webSocket));
  3. После этого мой отладчик ждет в очереди var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
  4. Когда сообщение отправлено с сервера, моя отладка продолжается правильно с результатом.

Мои проблемы и вопросы:

  1. В некоторых моментах срабатывает окончательно мой Connect метод, при котором websocket удаляется и мой слушатель освобождается. (Любые отправленные сообщения больше не перехватываются await webSocket.ReceiveAsync). При каких обстоятельствах это должно произойти и что я должен искать?

  2. Когда сообщение получено, я десериализовываю результат json в WebSocketNotification правильно, но моя функция javascript onMessage никогда не срабатывает. Что мне не хватает?

Обновление 2 О втором вопросе я заставил его работать, изменив

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

Я нахожу его королем странного, так как мой центр отчетов, который использует signalR, имел все свои методы на стороне сервера, как camelCase с заглавной буквой, и все свои методы на стороне клиента с простым camelCase. Почему этот случай отличается? Буду признателен за ответ на этот вопрос.

Обновление 3

Я вставил попытку поймать в моем методе OnMessage, как это

    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://stackru.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 переходит к окончательно и удаляется, я получаю следующую ошибку и трассировку стека

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

Я думаю, я мог бы просто справиться с этим и, возможно, снова открыть соединение. Теперь это в значительной степени мой единственный вопрос (если больше нечего искать):

Это лучший путь? Должен ли я узнать больше о том, почему мое соединение закрывается?

1 ответ

О вашем последнем вопросе вы можете узнать из этого поста.

Кажется, что WebSockets может закрываться по многим причинам, поэтому лучший способ - это обработать закрытие WebSocket и, возможно, открыть его снова.

Однако из ответов следует, что вам также следует проверить порт завершения ввода-вывода сервера IIS.

Одним из способов обработки вашего дела, предоставленного @user4624881, может быть использование IAsyncResult.AsyncWaitHandle.WaitOne() для обработки абортов через веб-сокет.

Как это:

Stream s = this.GetStream();
IAsyncResult ar = s.BeginWrite(data, 0, data.Length, SendAsync, state);
if (!ar.IsCompleted)
    ar.AsyncWaitHandle.WaitOne();
Другие вопросы по тегам