Runtime: ReceiveAsync cancellation throws WebSocketException and terminates the socket

Created on 6 Jan 2016  路  11Comments  路  Source: dotnet/runtime

await Socket.ReceiveAsync( new ArraySegment( Buffer ), Ct );
cannot be cancelled on a ClientWebSocket when Ct is the CancellationToken.
Instead of throwing a OperationCanceledException and keeping the socket alive, the following exception is thrown and the socket is aborted:

System.Net.WebSockets.WebSocketException: The 'System.Net.WebSockets.InternalClientWebSocket' instance cannot be used for communication because it has been transitioned into the 'Aborted' state. ---> System.Net.Http.WinHttpException: The operation has been canceled

This is true for both the ClientWebSocket and on a server socket when WebListener is used.
When Kestrel is used, ReceiveAsync CAN be cancelled on the SERVER, the cancellation takes about 10 seconds though.

Minimal solution with both the client and server part to reproduce on Stack Overflow: http://stackoverflow.com/questions/34634652/dnx-core-websocket-clientwebsocket-receiveasync-cancellation-is-not-working-p

area-System.Net bug

All 11 comments

cc: @davidsh, @cipop

@zeroskyx have you tried this in .Net Desktop? We've tried to be on parity with the current Desktop implementation as much as possible.

@CIPop I've just tried to use the full .NET 4.6 ClientWebSocket and it does work:
Client: Connecting...
Client: Receiving...
Operation cancelled.
Press any key to close...

I have updated the sample solution including the console project: https://onedrive.live.com/redir?resid=1DDFA1793BD83CB5!282492&authkey=!AMCP1uOI78VqCHY&ithint=file%2czip

Just run the dnx solution which only starts the server first, then jump into the console which uses the same Client file.

Thanks @zeroskyx!

We'll have to investigate if the underlying Win32 API supports cancellation in the described scenario and if it does, ensure the same behavior as on Desktop.

@karelz this looks like a behavior difference between .Net Framework and Core. I'd like us to review it for netstandard2.0.

Possibly related to dotnet/corefx#13169

We should investigate the behavior difference in 2.0.

Instead of throwing a OperationCanceledException and keeping the socket alive, the following exception is thrown and the socket is aborted:

There are 2 aspects to this issue. One, is about a different exception sometimes being thrown when the .ReadAsync() is cancelled. .NET Framework appears to always throw back an OperationCanceledException regardless of any race condition on the cancellation token. The current .NET Core code seems to thrown OperationCanceledException iff. the I/O hasn't started. But if it has started and the cancellation comes in later, then we appear to throw a WebException. This difference in behavior is something we should fix. We should always throw OperationCanceledException regardless of race conditions.

The second aspect of this issue is this:

and keeping the socket alive, the following exception is thrown and the socket is aborted:

I don't think .NET Framework guarantees that the socket will still be usable after a cancellation operation. Typcially, this is true even for other I/O APIs. I suppose if the cancellation token where already set when the ReadAsync() was called, then perhaps the I/O wouldn't be started and thus a subsequent ReadAsync() might work after the first one returned OperationCanceledException. But depending on race conditions, the socket might not be useable.

We should test explicitly what the behavior on .NET Framework is regarding the future usability of the socket after a prior cancellation of ReadAsync(). Then we'll know what behavior to match on that.

Umm, has this landed in .NET Core 2.0 for Windows already? This bug is still occuring here.

has this landed in .NET Core 2.0 for Windows already?

Yes.

This bug is still occuring here.

.NET Core now fully using managed websocket. If you observe a bug, please open a new issue and attach a working repro.

My .NET Core has some WinHttpWebSocket in its stack trace, so I'm not sure this is correct. Unfortunately I don't have repros yet, just my complex application and I'm still trying to figure out what happens. I have found other trouble with WebSockets though that I will report in another issue.

Was this page helpful?
0 / 5 - 0 ratings