NetworkStream.DataAvailable property does not return the right result when using SslStream

2.4k views Asked by At

I've got an application with a persistent socket (it's open when the application starts and closed along with the application).

This socket is used by a server to push some data.

Since this connection could be either HTTP or HTTPS, I wrote this code to initialize my objects:

s_tcpClient = new TcpClient(s_server.CometIp, s_server.CometPort);
s_tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, false);
s_tcpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, CST_STREAM_BUFFER_SIZE);
s_tcpClient.ReceiveTimeout = 0;

s_networkStream = s_tcpClient.GetStream();

s_cometStreamReader = null;

if (s_server.Protocol == "HTTPS")
{
    SslStream sslStream = new SslStream(
        s_tcpClient.GetStream(),
        false,
        new RemoteCertificateValidationCallback(ValidateServerCertificate)
    );

    sslStream.AuthenticateAsClient(s_server.CometIp);
    s_cometStreamReader = new StreamReader(sslStream, Encoding.UTF8, true, CST_STREAM_BUFFER_SIZE);
    s_cometStream = sslStream as Stream;
}
else
{
    s_cometStreamReader = new StreamReader(s_networkStream, Encoding.UTF8, true, CST_STREAM_BUFFER_SIZE);
    s_cometStream = s_networkStream as Stream;
}

This way, I can handle both HTTP and HTTPS protocols.

Then, I've got this loop (in a thread) which waits for data to be available in the socket:

while (s_networkStream.DataAvailable)
{
    numberOfBytesRead = s_cometStream.Read(buffer, 0, buffer.Length);
    message.Append(Encoding.UTF8.GetString(buffer, 0, numberOfBytesRead));
    ExtractMessages(ref message);
}

Here are my 2 questions:

  1. The documentation says that the Read method returns 0 if no data is available. But in my case, Read either returns a number > 0 (when some data have been read) or throws an exception because it timed out... What am I missing, here? Under which circumstances does Read return 0?

  2. When my connection uses HTTP protocol, this code works fine. I receive everything and if a message is bigger than my buffer size (CST_STREAM_BUFFER_SIZE), DataAvailable remains true, and the loop reads all remaining data. But when I try this with HTTPS, it works only for messages smaller than my buffer. If a message if bigger, the method Read first reads CST_STREAM_BUFFER_SIZE bytes and then DataAvailable becomes false, although some bytes are obviously ready to be read. And in this case, receiving another message "unstucks" the remaining data because DataAvailable becomes true again. What am I doing wrong? Why does not DataAvailable act the same in both cases?

Any help would be appreciated.

1

There are 1 answers

2
Marc Gravell On

It would only return 0 if the stream is closed, and hence no more data will ever come. Otherwise a positive number if data could be read, which may involve blocking if no data is yet available. If CanTimeout is true, the operation will throw after ReadTimeout has passed with no data available.

It sounds like maybe the socket has been left open and/or you are trying to read too much of you have an protocol that sends multiple chunks of data in a conversation.

http://msdn.microsoft.com/en-us/library/system.io.stream.readtimeout.aspx