hultqvist
hultqvist

Reputation: 18429

Close WebSocket Threadsafe

Using System.Net.WebSockets.WebSocket I have a read loop in one thread.

var result = await ws.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

From another thread I Write.

lock (ws)
    ws.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, CancellationToken.None).Wait();

This works fine.

My question is, how can I cleanly close this connection?

Any attempts to close result in an exception:

await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Bye", CancellationToken.None);
InvalidOperationException: 'Concurrent reads are not supported.'

I've tried to pass a CancellationToken to ws.ReceiveAsync so that I could call ws.CloseAsync from inside the receive loop. That didn't interrupt the ReceiveAsync call, it returned when the next message arrived.

Is it possible to cleanly close the socket outside the read loop. I could implement a "Close connection" message but that seems overkill when there is a concept of sending a close message in the WebSocket protocol.

Upvotes: 0

Views: 509

Answers (2)

hultqvist
hultqvist

Reputation: 18429

In that case, you'll need to call CloseOutputAsync and handle the CloseSent message type in your reads. – Stephen Cleary Mar 14 at 13:49

You can call CloseOutputAsync while another thread is waiting on ReceiveAsync.

Upvotes: 0

Matthias247
Matthias247

Reputation: 10396

I don't see a concurrent read in this code - if you are calling ReceiveAsync only from a single thread and if you await the result of the operation before starting the next one there should not be this exception. Where is it thrown?

Suggestion for the writing part: If you use a lock and a blocking write (with .Wait()) you are blocking a full thread (which potentially runs other handles), which might not be what you want. Better use SemaphoreSlim instead, where you can do:

await semaphore.WaitAsync();
try
{
    await ws.SendAsync();
}
finally
{
    semaphore.Release();
}

If the text of the error message is not precise and it actually complains about a concurrent Send and Close then you could use the same semaphore also for protecting the Close call:

await semaphore.WaitAsync();
try
{
    await ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "Bye", CancellationToken.None);
}
finally
{
    semaphore.Release();
}

Upvotes: 1

Related Questions