C # WebSockets Многоадресные уведомления асинхронно - PullRequest
0 голосов
/ 31 мая 2019

У меня есть основное приложение Asp.NET.Startup.Configure() в основном содержит этот код

app.UseWebSockets();
app.Use(async (httpContext, next) =>
{
    // If the request is a WebServerRequest, handle it as such ...
    if (httpContext.WebSockets.IsWebSocketRequest)
    {
        ClientHandler h = new ClientHandler(httpContext);

        if (h.IsWebsockOpen)
        {
            await h.Handle();
        }
        else
        {
            httpContext.Response.StatusCode = 400;
        }
    }

    // ... otherwise just hand the request to the next element in chain
    else
    {
        await next();
    }
});

Внутри h.Handle() клиент должен регистрироваться на ClientManager, который, в свою очередь, многоадресно передает, что новый клиент подключил вот так

public async Task Multicast<T>(List<ClientHandler> l, Msg<T> m)
{
    foreach (ClientHandler h in l)
    {
        if (h.IsWebsockOpen)
        {
            await h.webSocket.SendAsync(
                System.Text.Encoding.UTF8.GetBytes(m.ToString()),
                System.Net.WebSockets.WebSocketMessageType.Text,
                true,
                System.Threading.CancellationToken.None);
        }
    }
}

Теперь мне интересно, безопасно ли это делать.Я могу представить себе сценарий, когда два клиента соединяются одновременно, в результате чего h.webSocket.SendAsync вызывается дважды одновременно, что недопустимо, как сказано в

https://docs.microsoft.com/en-us/dotnet/api/system.net.websockets.websocket.sendasync?view=netframework-4.8

Замечания Этооперация не заблокирует.Возвращенный объект Task завершится после отправки данных в WebSocket.

Для каждого объекта WebSocket параллельно поддерживается только одна отправка и один прием.

Обтекание h.webSocket.SendAsync в lock -состоянии кажется невозможным из-за ключевого слова await.

Как я могу сделать мой код безопасным?Связанные вопросы либо не используют WebSockets, либо используют разные платформы, для которых применяются механизмы.

1 Ответ

1 голос
/ 31 мая 2019

Вы можете использовать семафор здесь, в частности SemaphoreSlim. Я бы предложил создать SendAsync метод для вашего ClientHandler класса и передать по нему все запросы - т.е. вызвать его из вашего Multicast метода.

Содержимое вашего ClientHandler будет тогда примерно таким:

class ClientHandler() {
    private readonly SemaphoreSlim _sendLock;
    public ClientHandler(HttpContext context) {
        _sendLock = new SemaphoreSlim(1, 1);
        //....
    }

    public async Task SendAsync(string msg) {
        await _sendLock.WaitAsync();
        try {
            await webSocket.SendAsync( 
                System.Text.Encoding.UTF8.GetBytes(msg.ToString()),
                System.Net.WebSockets.WebSocketMessageType.Text,
                true,
                System.Threading.CancellationToken.None);
        } finally {
            _sendLock.Release();
        }
    }
}

SemaphoreSlim - это IDisposable, поэтому вам нужно позаботиться об этом, а его метод WaitAsync имеет перегрузки для токенов отмены и / или тайм-ауты, которые могут быть вам полезны.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...