WebSocketClient на c#

Разбираюсь с Blazor. Столкнулся с задачей - нужно иметь постоянно работающий вебсокет клиент, который по мере получения сообщений будет их транслировать на веб страничку, ну и по командам со странички что-то отправлять серверу.

Все найденные примеры сводятся к тому что создали клиента, соединились с эхо-сервером, отправили сообщение, прочитали ответ и завершили работу..

Что делал: Сделал из этого таск, который подключается к серверу и в цикле ждёт сообщений. По прочтении просто выводит в лог. И сделал функцию которая отправляет сообщение. Вроде хорошо.

Размещал это в Program.cs подключается, слушает, всё вроде хорошо, но вызвать функцию отправки из *.razor не могу - CS8801

Перенёс все в .razoz, по событию со странички вызывается runWS (после чего sendWS хотел быть вызванным) - вебсокет клиент подключается, но далее, вполне логично ждёт сообщения от сервера, но сообщения нет - висим, ждём.

@code{
    private static ClientWebSocket ws;

    private static async Task DoClientWebSocket()
    {
        using ( ws = new ClientWebSocket())
        {
            Uri serverUri = new Uri("wss://echo.websocket.org/");

            await ws.ConnectAsync(serverUri, CancellationToken.None);

            while (ws.State == WebSocketState.Open )
            {
                //Receive buffer
                var receiveBuffer = new byte[2048];
                //Multipacket response
                var offset = 0;
                var dataPerPacket = 1024; //Just for example

                while (true)
                {
                    ArraySegment<byte> bytesReceived =
                                    new ArraySegment<byte>(receiveBuffer, offset, dataPerPacket);
                    WebSocketReceiveResult result = await ws.ReceiveAsync(bytesReceived,
                                                                        source.Token);
                    offset += result.Count;
                    if (result.EndOfMessage)
                        break;
                }
                Console.WriteLine("\nWS message: {0}",
                                    Encoding.UTF8.GetString(receiveBuffer, 0,
                                                                offset));
            }
        }
    }


    public async Task runWS()
    {
        var taskWebConnect = Task.Run(() => DoClientWebSocket());

        taskWebConnect.Wait();
    }



    public async Task sendWS(string text)
    {
        ArraySegment<byte> arraySegment = new ArraySegment<byte>(Encoding.UTF8.GetBytes( text ));
        await ws.SendAsync(arraySegment, System.Net.WebSockets.WebSocketMessageType.Text, true, CancellationToken.None);
    }

    
}

Подскажите, как тут задачу параллельно запустить? Либо по событию с вебстарницы, либо сразу при старте приложения.


Ответы (1 шт):

Автор решения: Dev18

если использовать статический ClientWebSocket и статические методы, может привести к несоответствию допустимости null, так как статические члены не имеют доступа к состоянию экземпляра компонента Blazor, что возможно выдает ошибку CS8801

клиент должен асинхронно обрабатывать сообщения не блокируя главный поток UI

Используйте асинхронные методы для подключения и беспрерывного получения сообщений, чтобы UI не "зависал" в ожидании данных

Я немного подкоректировала ваше решение и провела различные тесты, отображая процессы на разор странице

using System.Net.WebSockets;
using System.Text;

namespace SomeProjectTestSocket.Services
{
    public class WebSocketClientService
    {
        private ClientWebSocket _webSocket;

        public event Action<string> OnMessageReceived;
        public event Action<string> OnStatusChanged;

        public async Task ConnectAsync(Uri serverUri, CancellationToken cancellationToken)
        {
            _webSocket = new ClientWebSocket();
            try
            {
                await _webSocket.ConnectAsync(serverUri, cancellationToken);
                OnStatusChanged?.Invoke("Connected");
                await ReceiveMessagesAsync(cancellationToken);
            }
            catch (Exception ex)
            {
                OnStatusChanged?.Invoke($"Error: {ex.Message}");
            }
        }

        private async Task ReceiveMessagesAsync(CancellationToken cancellationToken)
        {
            var buffer = new byte[2048];
            while (_webSocket.State == WebSocketState.Open)
            {
                try
                {
                    var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cancellationToken);
                    if (result.MessageType == WebSocketMessageType.Text)
                    {
                        var message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                        OnMessageReceived?.Invoke(message);
                        OnStatusChanged?.Invoke($"Received message: {message}");
                    }
                    else if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken);
                        OnStatusChanged?.Invoke("Closed");
                    }
                }
                catch (Exception ex)
                {
                    OnStatusChanged?.Invoke($"Error: {ex.Message}");
                    break;
                }
            }
        }

        public async Task SendMessageAsync(string message, CancellationToken cancellationToken)
        {
            if (_webSocket.State == WebSocketState.Open)
            {
                var buffer = Encoding.UTF8.GetBytes(message);
                await _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, cancellationToken);
                OnStatusChanged?.Invoke($"Sent message: {message}");
            }
            else
            {
                OnStatusChanged?.Invoke("WebSocket is not open");
            }
        }
    }
}
@page "/websocket"
@inject WebSocketClientService WebSocketService

<h3>WebSocket Example</h3>

<button @onclick="ConnectWebSocket">Connect</button>
<button @onclick="SendMessage">Send Message</button>

<p>Received message: @_receivedMessage</p>
<p>Status: @_statusMessage</p>

@code {
    private string _receivedMessage;
    private string _statusMessage;

    protected override void OnInitialized()
    {
        WebSocketService.OnMessageReceived += (message) =>
        {
            InvokeAsync(() =>
            {
                _receivedMessage = message;
                StateHasChanged();
            });
        };

        WebSocketService.OnStatusChanged += (status) =>
        {
            InvokeAsync(() =>
            {
                _statusMessage = status;
                StateHasChanged();
            });
        };
    }

    private async Task ConnectWebSocket()
    {
        _statusMessage = "Connecting...";
        await WebSocketService.ConnectAsync(new Uri("wss://ws.ifelse.io"), CancellationToken.None);
    }

    private async Task SendMessage()
    {
        _statusMessage = "Sending message...";
        await WebSocketService.SendMessageAsync("Hello, WebSocket!", CancellationToken.None);
    }
}
builder.Services.AddSingleton<WebSocketClientService>();

введите сюда описание изображения


также полезная информация :

  1. wss://ws.ifelse.io — Популярный эхо-сервер.
  2. wss://echo.websocket.org — Один из наиболее часто используемых эхо-серверов, но в последнее время может быть недоступен.
  3. wss://demos.kaazing.com/echo — Эхо-сервер от Kaazing, также широко используется для демонстраций и тестирования.
  4. wss://echo.websocket.events — Эхо-сервер, предоставляемый сервисом websocket.events.
  5. wss://ws.postman-echo.com/raw — Сервис Postman также предоставляет эхо-сервер, хорошо подходит для тестирования WebSocket соединений.

При работе с эхо-серверами важно помнить, что некоторые из них могут быть временно недоступны или работать с ограничениями из-за политик безопасности или загрузки сервера. Поэтому, если один из серверов не отвечает, рекомендуется попробовать другой. (к примеру вчера один из них не отвечал при тестах)

→ Ссылка