Understanding the NetworkStream.EndRead()-example from MSDN

jsf picture jsf · Oct 1, 2013 · Viewed 16.7k times · Source

I tried to understand the MSDN example for NetworkStream.EndRead(). There are some parts that i do not understand.

So here is the example (copied from MSDN):

// Example of EndRead, DataAvailable and BeginRead.

public static void myReadCallBack(IAsyncResult ar ){

    NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;
    byte[] myReadBuffer = new byte[1024];
    String myCompleteMessage = "";
    int numberOfBytesRead;

    numberOfBytesRead = myNetworkStream.EndRead(ar);
    myCompleteMessage = 
        String.Concat(myCompleteMessage, Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));    

    // message received may be larger than buffer size so loop through until you have it all.
    while(myNetworkStream.DataAvailable){

        myNetworkStream.BeginRead(myReadBuffer, 0, myReadBuffer.Length, 
                                               new AsyncCallback(NetworkStream_ASync_Send_Receive.myReadCallBack), 
                                                   myNetworkStream);  

    }

    // Print out the received message to the console.
    Console.WriteLine("You received the following message : " +
                                myCompleteMessage);
}

It uses BeginRead() and EndRead() to read asynchronously from the network stream. The whole thing is invoked by calling

myNetworkStream.BeginRead(someBuffer, 0, someBuffer.Length, new AsyncCallback(NetworkStream_ASync_Send_Receive.myReadCallBack), myNetworkStream);  

somewhere else (not displayed in the example).

What I think it should do is print the whole message received from the NetworkStream in a single WriteLine (the one at the end of the example). Notice that the string is called myCompleteMessage.

Now when I look at the implementation some problems arise for my understanding.

First of all: The example allocates a new method-local buffer myReadBuffer. Then EndStream() is called which writes the received message into the buffer that BeginRead() was supplied. This is NOT the myReadBuffer that was just allocated. How should the network stream know of it? So in the next line numberOfBytesRead-bytes from the empty buffer are appended to myCompleteMessage. Which has the current value "". In the last line this message consisting of a lot of '\0's is printed with Console.WriteLine.

This doesn't make any sense to me.

The second thing I do not understand is the while-loop.

BeginRead is an asynchronous call. So no data is immediately read. So as I understand it, the while loop should run quite a while until some asynchronous call is actually executed and reads from the stream so that there is no data available any more. The documentation doesn't say that BeginRead immediately marks some part of the available data as being read, so I do not expect it to do so.

This example does not improve my understanding of those methods. Is this example wrong or is my understanding wrong (I expect the latter)? How does this example work?

Answer

Jeroen van Langen picture Jeroen van Langen · Oct 1, 2013

I think the while loop around the BeginRead shouldn't be there. You don't want to execute the BeginRead more than ones before the EndRead is done. Also the buffer needs to be specified outside the BeginRead, because you may use more than one reads per packet/buffer.

There are some things you need to think about, like how long are my messages/blocks (fixed size). Shall I prefix it with a length. (variable size) <datalength><data><datalength><data>

Don't forget it is a Streaming connection, so multiple/partial messages/packets can be read in one read.

Pseudo example:

int bytesNeeded;
int bytesRead;

public void Start()
{
    bytesNeeded = 40; // u need to know how much bytes you're needing
    bytesRead = 0;

    BeginReading();
}

public void BeginReading()
{
    myNetworkStream.BeginRead(
        someBuffer, bytesRead, bytesNeeded - bytesRead, 
        new AsyncCallback(EndReading), 
        myNetworkStream);
}

public void EndReading(IAsyncResult ar)
{
    numberOfBytesRead = myNetworkStream.EndRead(ar);

    if(numberOfBytesRead == 0)
    {
        // disconnected
        return;
    }

    bytesRead += numberOfBytesRead;

    if(bytesRead == bytesNeeded)
    {
        // Handle buffer
        Start();
    }
    else
        BeginReading();
}