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 шт):
если использовать статический 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>();
также полезная информация :
- wss://ws.ifelse.io — Популярный эхо-сервер.
- wss://echo.websocket.org — Один из наиболее часто используемых эхо-серверов, но в последнее время может быть недоступен.
- wss://demos.kaazing.com/echo — Эхо-сервер от Kaazing, также широко используется для демонстраций и тестирования.
- wss://echo.websocket.events — Эхо-сервер, предоставляемый сервисом websocket.events.
- wss://ws.postman-echo.com/raw — Сервис Postman также предоставляет эхо-сервер, хорошо подходит для тестирования WebSocket соединений.
При работе с эхо-серверами важно помнить, что некоторые из них могут быть временно недоступны или работать с ограничениями из-за политик безопасности или загрузки сервера. Поэтому, если один из серверов не отвечает, рекомендуется попробовать другой. (к примеру вчера один из них не отвечал при тестах)