tags:

views:

50

answers:

3

In the code below I have a StreamReader reading from a network stream. This code normally will run fine for days. I ran into a problem where all of a sudden StreamReader.ReadLine() started returning null.

According to Microsoft documentation StreamReader.ReadLine() will return null when it has reached the end of the input stream. This doesn't make sense to me when the underlying stream is a NetworkStream. Shouldn't ReadLine() just block until the network stream receives data?

This is the first time I ran into this problem and I have not been able to duplicate it. What could cause this?

Context: the application receives CDR records from a phone switch. The phone switch connects to the application and sends plain old text records. After the switch connects it will remain connected and keep sending records for eternity unless something breaks.

    private void ProcessClient(TcpClient client)
    {
        try
        {
            using (NetworkStream stream = client.GetStream())
            {
                using (StreamReader reader = new StreamReader(stream))
                {
                    //continue processing while service is on
                    while (m_RunService & client.Connected)
                    {

                        string curLine = reader.ReadLine();

                        //code here does stuff to string
                        //will catch any exceptions that have to do with 
                        //processing the string
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //write to log
        }
    }

Here is the code that starts the listener:

    private void Listen()
    {
        try
        {
            while (m_RunService)
            {
                try
                {
                    m_TcpClient = m_TcpListener.AcceptTcpClient();

                    //run on same thread, should only ever be 1 cnx at a time
                    ProcessClient(m_TcpClient);
                }
                catch (Exception ex)
                {
                    //write to log
                }
                finally
                {
                    m_TcpClient.Close();
                }
            }
        }
        finally
        {
            m_TcpListener.Stop();
        }        
    } 
A: 

That pretty much sounds like the stream has closed. Otherwise yes: it would block. My guess: network #fail

Marc Gravell
+1  A: 

The StreamReader will block until it receives data or the connection is closed. It sounds like an exception occurred at the server side, it closed the connection, and the client side received no data.

Jon Skeet
how should I check for this scenario?
J Cooper
@J Cooper: Well, would you *always* expect output in the non-error case? If so, then you could check for it by seeing whether or not you get any results!
Jon Skeet
A: 

If the NetworkStream does not have data available, the call to ReadLine() will return null because it assumes it has reached the end of the stream.

Try checking NetworkStream.CanRead and NetworkStream.DataAvailable before calling ReadLine. If the connection has not closed, manually block for a time then try your read again.

EDIT You can check to see if the connection is still open by making a Send or using the underlying Socket:

The Connected property gets the connection state of the Socket as of the last I/O operation. When it returns false, the Socket was either never connected, or is no longer connected.

The value of the Connected property reflects the state of the connection as of the most recent operation. If you need to determine the current state of the connection, make a nonblocking, zero-byte Send call. If the call returns successfully or throws a WAEWOULDBLOCK error code (10035), then the socket is still connected; otherwise, the socket is no longer connected.

Brazzle
that doesn't make sense, when it works as expected ReadLine() will block for as long as it takes for the phone switch to send another record, which could be serveral minutes. ReadLine() does not return null then, it just blocks.
J Cooper
MSDN docs do not explicitly state that `ReadLine` blocks, but it does say it will return if it encounters an `IOException`, `OutOfMemoryException`, or it reaches the end of the input stream. Can't add much more than that. One of those three things is happening.
Brazzle
I think it's clicking with me. So basically if I start receiving null then I should run a check to see if I can send a message. Doesn't my attempt to receive a message (my call to StreamReader.readline()) count as an IO operation? (which should then update the Connected property)
J Cooper
I believe it does, so `Connected` should work, but I can't say for sure.
Brazzle