Reputation: 18429
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
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
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