How to get Many Websockets to run in parallel efficiently

Viewed 973

I'm writing a system that could potentially have many thousands of websockets open simultaneously and I can't seem to figure out how to get them all to run efficiently. I don't believe I'm using the Task properly with the async method. I set the thread to be long running because the listener basically runs forever. My problem seems to be that if I don't put in a delay I get very high CPU usage due to the await on the receive not blocking and I end up with many tight loops. I'm still fairly new with the TPL code, so any advice is appreciated.

Here's a reduced version of my code:

public class MyListener
{
    private const int ReceiveChunkSize = 1024;
    ClientWebSocket ws;
    public async Task ConnectWS()
    {
       ws = new ClientWebSocket();
       await ws.ConnectAsync(uri, CancellationToken.None);
       var task = new Task(() => StartListen(), TaskCreationOptions.LongRunning);
       task.Start();
    }

    private async void StartListen()
    {
        string m = "";
        var buffer = new byte[ReceiveChunkSize];
        bool reconnect = false;
        try
        {
            while (ws.State == WebSocketState.Open)
            {
                var stringResult = new StringBuilder();
                WebSocketReceiveResult result;
                do
                {
                    result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), _cancellationToken);

                    if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await
                            ws.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);

                    }
                    else
                    {
                        var str =Encoding.UTF8.GetString(buffer, 0, result.Count);
                        stringResult.Append(str);
                    }
                    /* Why is this delay necessary? */
                    await Task.Delay(100);
                } while (!result.EndOfMessage);
                string msg = stringResult.ToString();
                System.Diagnostics.Debug.WriteLine(msg);                
            }
        }
        catch (Exception ex)
        {

        }
        finally
        {
            ws.Dispose();
        }
    }
}

EDIT: Here is my send async code. If I wanted to just reply three times with the message sent would it be best to use:

await SendMessageAsync(msg);
await SendMessageAsync(msg);
await SendMessageAsync(msg);

I also seemed to have high CPU when using ws.SendAsync. The debugger always seemed to pause on that line.

private async Task SendMessageAsync(string message)
{
    try
    {
        if (ws.State != WebSocketState.Open)
        {
            return;   
        }    
        var messageBuffer = Encoding.UTF8.GetBytes(message);
        var messagesCount =    (int)Math.Ceiling((double)messageBuffer.Length/SendChunkSize);

        for (var i = 0; i < messagesCount; i++)
        {
            var offset = (SendChunkSize * i);
            var count = SendChunkSize;
            var lastMessage = ((i + 1) == messagesCount);

            if ((count * (i + 1)) > messageBuffer.Length)
            {
                count = messageBuffer.Length - offset;
        }

        await ws.SendAsync(new ArraySegment<byte>(messageBuffer, offset, count), WebSocketMessageType.Text, lastMessage, _cancellationToken);

    }
    catch (Exception ex)
    {

    }
}

EDIT: After more testing I'm definitely seeing a lot of high CPU usage and every time I pause it's on the sendasync. Right now I have about 400 open sockets and each one sends a message every 30 seconds using a System.Threading.Timer callback.

0 Answers
Related