How to read all requested data using NetworkStream.BeginRead?

7.4k views Asked by At

There is a code of async server. Client sends Header - size of Data Block + Data Block.

Server reads asynchronously first Header and then Data Block.

I need, after I read Data Block run the BeginRead for Header reading part, to make threads async.

PROBLEM:

When I got DataCallBack, in line:

int bytesRead = ns.EndRead(result);

I get not all buffer i asked to read in

mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);

If client send 1MB of Data I can get different number of "bytesRead".

QUESTION:

How to force "BeginRead" to read all data from connection. It should cause the new loop of Header - Data.

MyClient - simply wrapper over TcpClient;

CODE:

    public  void DoAcceptTcpClientCallback(IAsyncResult ar)
    {
        TcpListener listener = (TcpListener)ar.AsyncState;
        TcpClient client = listener.EndAcceptTcpClient(ar);
        client.NoDelay = false;
       // client.ReceiveBufferSize = 1024*1024;
        listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), listener);
        MyClient mc = new MyClient(client);
        ContinueRead(0,mc);
    }

    public void ContinueRead(int size, MyClient mc)
    {
        if (size != 0)
        {
            mc.DataBuffer = new byte[size];
            mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);
        }
        mc.Client.GetStream().BeginRead(mc.HeaderBuffer, 0, 4, new AsyncCallback(HeaderCallBack), mc);

    }

    private void HeaderCallBack(IAsyncResult result)
    {
        MyClient mc = (MyClient)result.AsyncState;
        NetworkStream ns = mc.Stream;
        int bytesRead = ns.EndRead(result);
        if (bytesRead == 0)
            throw new Exception();
        mc.TotalLengs =  BitConverter.ToInt32(mc.HeaderBuffer, 0);
        ContinueRead(mc.TotalLengs, mc);

    }
    private void DataCallBack(IAsyncResult result)
    {
        MyClient mc = (MyClient)result.AsyncState;
        NetworkStream ns = mc.Stream;
        int bytesRead = ns.EndRead(result);
        if (bytesRead == 0)
            throw new Exception();

BAD CODE - MAKES ASYNC READING - SYNC

        while (bytesRead < mc.TotalLengs)
        {
            bytesRead += ns.Read(mc.DataBuffer, bytesRead, mc.TotalLengs - bytesRead);
        }

END BAD CODE

        ContinueRead(0, mc);
        ProcessPacket(mc.DataBuffer, mc.IP);
    }
1

There are 1 answers

4
Idle_Mind On

"If client send 1MB of Data I can get different number of "bytesRead"."

Yes...this is simply how TCP works under the hood. You can't change this. TCP guarantees the order of packets, not how they are grouped. The hardware and traffic conditions along the route the packets travel determine how that data is grouped (or un-grouped).

"How to force "BeginRead" to read all data from connection."

TCP has no idea how much data is being sent. As far as it is concerned, the connection is simply an endless stream of bytes; therefore it cannot read "all data" since there is no end to the data (from its perspective). TCP also has no notion of what a "complete message" is with respect to your application. It is up to you, the programmer, to develop a protocol that allows your application to know when all data has been sent.

If you are expecting a certain number of bytes, then keep a running sum of the values returned by EndRead() and stop when that magic number is hit.