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);
}
}
}
}
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).
DataReader
/DataWriter
are not concerned with "connections." They're really justBitConverter
, only designed better this time around.I would guess that the reason
StreamSocket
doesn't have a "connected" property is becauseSocket.Connected
is nearly useless and definitely misleading.I would try using
StreamSocket.InputStream.ReadAsync
directly instead of usingDataReader
, since you are just reading bytes anyway. It sounds like you may have uncovered a bug inDataReader
, which you should report on Microsoft Connect ifInputStream.ReadAsync
works as expected. Also see this related forum post.