Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling WinRT StreamSocket disconnects (both server and client side)

I have an application I'm writing for Windows 8/WinRT that uses the StreamSocket API to do a streaming connection to a server. That is to say, the server streams data to the client, sometimes with meta tags, and can disconnect at any time.

The problem I'm having is that I have no idea how to detect when the server has disconnected. There don't appear to be any events or properties on the StreamSocket class, either its input or output streams, or on the DataReader/DataWriter classes that have anything to do with connection status.

On top of that, the DataReader method ReadAsync is not failing after the server side disconnects from the client. Instead, the operation succeeds as far as I can tell, and the data it fills into its buffer is just the last thing the server sent to it (i.e. it's not clearing its internal buffer, even though I can see that it has "consumed" the buffer each time I call ReadByte). It does this for every subsequent call to ReadAsync - refilling the buffer with what the server sent last before it disconnected. Here is a simplified version of the code:

    public async Task TestSocketConnectionAsync()
    {
        var socket = new StreamSocket();
        await socket.ConnectAsync(new HostName(Host), Port.ToString(),
            SocketProtectionLevel.PlainSocket);
        var dr = new DataReader(socket.InputStream);
        dr.InputStreamOptions = InputStreamOptions.Partial;

        this.cts = new CancellationTokenSource();
        this.listenerOperation = StartListeningAsync(dr, cts);
    }

    public async Task StartListeningAsync(DataReader dr, CancellationTokenSource cts)
    {
        var token = cts.Token;
        while (true)
        {
            token.ThrowIfCancellationRequested();
            var readOperation = dr.LoadAsync(1024);
            var result = await readOperation;
            if (result <= 0 || readOperation.Status != Windows.Foundation.AsyncStatus.Completed)
            {
                cts.Cancel(); // never gets called, status is always Completed, result always > 0
            }
            else
            {
                while (dr.UnconsumedBufferLength > 0)
                {
                    byte nextByte = dr.ReadByte();

                    // DriveStateMachine(nextByte);
                }
            }
        }
    }
like image 615
Jeremy Bell Avatar asked Apr 10 '12 21:04

Jeremy Bell


1 Answers

That is to say, the server streams data to the client, sometimes with meta tags, and can disconnect at any time. The problem I'm having is that I have no idea how to detect when the server has disconnected.

A "graceful" socket closure can be detected by the other side as a 0-length read. That is, it just acts like a regular end-of-stream.

An "abortive" socket closure is more tricky. You have to send data to detect that the other side has closed, and once that write fails, any additional reads or writes should fail (with an exception). If your protocol doesn't permit you to send data, then you'll have to assume the connection is bad after a timeout expires and just close it. :(

Depending on the application "abortive" socket closure may be normal - in particular, very busy servers may be written to clamp shut their connections because it allows them to reclaim resources more quickly (avoiding the four-step socket shutdown handshake).

There don't appear to be any events or properties on the StreamSocket class, either its input or output streams, or on the DataReader/DataWriter classes that have anything to do with connection status.

DataReader/DataWriter are not concerned with "connections." They're really just BitConverter, only designed better this time around.

I would guess that the reason StreamSocket doesn't have a "connected" property is because Socket.Connected is nearly useless and definitely misleading.


I would try using StreamSocket.InputStream.ReadAsync directly instead of using DataReader, since you are just reading bytes anyway. It sounds like you may have uncovered a bug in DataReader, which you should report on Microsoft Connect if InputStream.ReadAsync works as expected. Also see this related forum post.

like image 185
Stephen Cleary Avatar answered Oct 16 '22 05:10

Stephen Cleary