Dela via


Is it a bug?: ClientWebSocket

The following program always fails for me with the web socket reaching the aborted state within a couple seconds.

class Program
{
    static void Main(string[] args)
    {
        ClientWebSocket socket = new ClientWebSocket();
        socket.ConnectAsync(new Uri("ws://localhost:8085/Echo.ashx"), CancellationToken.None).Wait();
        var ob = new ArraySegment<byte>(Encoding.UTF8.GetBytes("hello"));
        var buffer = new ArraySegment<byte>(new byte[1024]);
        var sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 100000; i++)
        {
            socket.SendAsync(ob, WebSocketMessageType.Text, true, CancellationToken.None);
            var rr = socket.ReceiveAsync(buffer, CancellationToken.None).Result;
            if (rr.EndOfMessage)
            {
                Console.WriteLine("received complete message: " + Encoding.UTF8.GetString(buffer.Array, 0, rr.Count));
            }
        }
        sw.Stop();
        Console.WriteLine("the end." + sw.ElapsedMilliseconds);
        Console.ReadLine();
    }
}

However, if I add a Wait() to the end of SendAsync(), then I can send/receive on the socket for a LONG time with no problems.
I was initially mystified as to why this happens. In fact I thought part of the design of websockets was that clients and servers can choose to send whenever they want, and for instance, clients can even pipeline multiple requests to the server.

InnerException: System.Net.WebSockets.WebSocketException
       HResult=-2147467259
       Message=The WebSocket is in an invalid state ('Aborted') for this operation. Valid states are: 'Open, CloseSent'
       StackTrace:
            at System.Net.WebSockets.WebSocket.ThrowOnInvalidState(WebSocketState state, WebSocketState[] validStates)
            at System.Net.WebSockets.WebSocketBase.<ReceiveAsyncCore>d__1.MoveNext()
       InnerException:

Am I misunderstanding something?

Well this time I turn on first-chance exception debugging, and find that there is a previous exception which pushes the ClientWebSocket to the aborted state:

There is already one outstanding 'SendAsync' call for this WebSocket instance. ReceiveAsync and SendAsync can be called simultaneously, but at most one outstanding operation for each of them is allowed at the same time.

But wait… how can that be? My server only sends me a message in response to my request? So this can only fail if the socket completes receive the response to a message before it has finished sending the message… but actually that is possible! Because we never know whether the receive or send operation’s completion callback will be dispatched first by the underlying technology stack.

OK. Well that’s certainly helpful, I just  have to fix the code slightly:

var sendResult = socket.SendAsync(ob, WebSocketMessageType.Text, true, CancellationToken.None);
var rr = socket.ReceiveAsync(buffer, CancellationToken.None).Result;
sendResult.Wait();
if (rr.EndOfMessage)
{
    Console.WriteLine("received complete message: " + Encoding.UTF8.GetString(buffer.Array, 0, rr.Count));
}

And my client app runs happily forever without aborting. Good.

Still, I wonder – is this throwing on concurrent sends behavior really the right thing? Personally, I was very surprised with this - I was expecting that outgoing requests on a websocket would be queued at a lower level – should I really need to implement queuing in my application layer? But this bug may be in the eye of the beholder…

Comments

  • Anonymous
    April 01, 2014
    you need to either await or wait on the .sendAsync it doesn't allow you to call sendAsync again before the underlying native api done sending the current message.