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.