I'm all in favour of multithreading and asyncronous tasks, but letting go of control isn't my cup of tea.
I'm working on a project where one process is communicating with multiple targets over websockets, and sending should obviously be as speedy as possible, but has to be synced with each other. Think in line of streaming a videostream to multiple devices simontaniously. Thus, I don't want to continue sending the next frame before the first one has been receieved by the target.
When using .net WebSockets, I've tried this both with HttpListner and the Kestrel implementation (which I guess is more or less the same), then the socket.SendAsync seems to be receiving the data and buffering it locally before sending it, returning TaskCompleted before its actually sent. This means that the send task basically completes instantly.
I "could" of course implement an ACK schema where the recipient sends an ACK when the whole packet has been received, but that would in itself slow down the process, which I would like to avoid as this is already known by the system itself, as it does try to send the queued up packages continously.
This is basically the code responsible for sending the data, in chunks of 2048b:
Log.Info($"Send {sendBytes.Length}b");
if (socket!.State == WebSocketState.Open)
{
await socket.SendAsync(
sendBuffer,
WebSocketMessageType.Binary,
endOfMessage: true,
cancellationToken: cts
);
}
Log.Info($"Done ");
I would assume the await here would actually wait for the transmission to complete, which is doesn't do, but it seems to await the task completedness.
And on the recipient side this is processing the data, with the debug responses before and after:
wsClientSession.send("REC " + (String)payloadLength + "b");
ProcessFrame(payload, payloadLength);
wsClientSession.send("ACK " + (String)payloadLength + "b");
The logging on the sender side shows this:
- Thread 28 is the sending thread, which completes impossibly fast. It needs to send 6198 bytes and chuncks this up in 4 transmissions. It is obviously all done before the first packet has left the server.
- The remaining threads are the async recipients of the debug responses.
- (size differences are due to the encryption of the data)
2022-02-07 16:03:34.8887 threadid:28 # Info # Send 6198b to 5d8263c6-909c-47a1-b4d0-57d74d6a4665
2022-02-07 16:03:34.8887 threadid:28 # Info # Send 2048b
2022-02-07 16:03:34.8887 threadid:28 # Info # Done
2022-02-07 16:03:34.8887 threadid:28 # Info # Send 2048b
2022-02-07 16:03:34.8887 threadid:28 # Info # Done
2022-02-07 16:03:34.8887 threadid:28 # Info # Send 2048b
2022-02-07 16:03:34.8887 threadid:28 # Info # Done
2022-02-07 16:03:34.8887 threadid:28 # Info # Send 128b
2022-02-07 16:03:34.8887 threadid:28 # Info # Done
2022-02-07 16:03:34.8887 threadid:28 # Info # Send done to 5d8263c6-909c-47a1-b4d0-57d74d6a4665
2022-02-07 16:03:35.0024 threadid:7 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - REC 2030b
2022-02-07 16:03:35.0090 threadid:7 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - ACK 2030b
2022-02-07 16:03:35.0090 threadid:17 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - REC 2030b
2022-02-07 16:03:35.0260 threadid:33 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - ACK 2030b
2022-02-07 16:03:35.0260 threadid:33 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - REC 2030b
2022-02-07 16:03:35.0260 threadid:30 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - ACK 2030b
2022-02-07 16:03:35.0373 threadid:17 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - REC 108b
2022-02-07 16:03:35.0373 threadid:4 # Info # 5d8263c6-909c-47a1-b4d0-57d74d6a4665 - ACK 108b
Now, is my understanding of this WebSocket implementation all wrong? Is it not possible to have a callback/event or similar on data sent completedness? Can I achieve this in another way? Executing this true synchronously would be a solution, but I don't seem to be able to force that.
GAAH!!