c# – How to properly append a fragmented tcp packet?

Question:

Hello.

There is some kind of server application, data is received asynchronously, the size of received packets is known. Sometimes packets arrive in chunks, in which case the read is repeated from the socket, for example, the packet size is 1994 bytes, a chunk of 1360 bytes has arrived, it remains to receive 634 bytes, I check socket.Available > 0 and that I see that there is no data (socket.Available = 0) , I read a new package and the rest of the package arrives? How to be? Maybe you should not do such a check, but just add the rest of it?

private void EndRecieveQ0(IAsyncResult result)
{
    try
    {
        Socket handler = this.StateConnection.workSocket;
        if (CheckSocketConnected() == false)
            return;
        int recievedBytes;
        try
        {
            recievedBytes = this.StateConnection.workSocket.EndReceive(result);
            receiveDone.Set();
        }
        catch
        {
            return;
        }
        this.StateConnection.ClientReceivedBytes += recievedBytes;
        if (this.StateConnection.ClientReceivedBytes == this.StateConnection.ExpectedPacketLength)
        {
            byte[] response = new byte[0];
            bool termination;
            new PacketHandler(this, this.StateConnection.ClientReceiveBuffer, out response, out termination);
            this.StateConnection.termination = termination;

            Array.Resize(ref this.StateConnection.ClientSendBuffer, response.Length);
            this.StateConnection.ClientSendBuffer = response;
            this.StateConnection.ClientSendBytes = 0;
            sendDone.Reset();
            this.StateConnection.workSocket.BeginSend(this.StateConnection.ClientSendBuffer, 0, this.StateConnection.ClientSendBuffer.Length, 0, new AsyncCallback(SendCallback), null);
            sendDone.WaitOne();
        }
        else
        {
            this.StateConnection.LeftToReceiveLength = (this.StateConnection.ExpectedPacketLength) - this.StateConnection.ClientReceivedBytes;

            if (handler.Available > 0)
            {
                receiveDone.Reset();
                this.StateConnection.workSocket.BeginReceive(
                    this.StateConnection.ClientReceiveBuffer,
                    this.StateConnection.ClientReceivedBytes,
                    this.StateConnection.LeftToReceiveLength,
                    SocketFlags.None,
                    EndRecieveQ0,
                    null);

                receiveDone.WaitOne(); //?
            }
            else
            {
                Logger.BufferPacketLog("[<-] " + "\n" + "handler.Available = " + handler.Available.ToString(), this.ID, 3);
                this.StateConnection.ClientReceiveBuffer = new byte[10];
                receiveDone.Reset();
                this.StateConnection.workSocket.BeginReceive(
                    this.StateConnection.ClientReceiveBuffer, 
                    0, 
                    this.StateConnection.ClientReceiveBuffer.Length, 
                    0,
                    NewPacketHeader, 
                    null);
                receiveDone.WaitOne();
            }
        }
    }
    catch (Exception e)
    {
        if (e is SocketException)
        {
            closeDone.Reset();
            termination(this.StateConnection.ID);
            closeDone.WaitOne();
            return;
        }
    }
}

Answer:

handler.Available shows only the fact that there is still some data for this application in the buffer of the network card, it makes no sense to rely on this fact.

What you have in if (handler.Available > 0) you need to leave, and what in else move to the end if (this.StateConnection.ClientReceivedBytes == this.StateConnection.ExpectedPacketLength)

Also, you don't need ManualResetEvent s receiveDone and sendDone .

private void EndRecieveQ0(IAsyncResult result)
{
    try
    {
        Socket handler = this.StateConnection.workSocket;
        if (CheckSocketConnected() == false)
            return;
        int recievedBytes;
        try
        {
            recievedBytes = this.StateConnection.workSocket.EndReceive(result);
            //receiveDone.Set();
        }
        catch
        {
            return;
        }
        this.StateConnection.ClientReceivedBytes += recievedBytes;
        if (this.StateConnection.ClientReceivedBytes == this.StateConnection.ExpectedPacketLength)
        {
            byte[] response = new byte[0];
            bool termination;
            new PacketHandler(this, this.StateConnection.ClientReceiveBuffer, out response, out termination);
            this.StateConnection.termination = termination;

            Array.Resize(ref this.StateConnection.ClientSendBuffer, response.Length);
            this.StateConnection.ClientSendBuffer = response;
            this.StateConnection.ClientSendBytes = 0;
            //sendDone.Reset();
            this.StateConnection.workSocket.BeginSend(this.StateConnection.ClientSendBuffer, 0, this.StateConnection.ClientSendBuffer.Length, 0, new AsyncCallback(SendCallback), null);
            //sendDone.WaitOne();

            // vvvvv Начинаем читать новый пакет
            this.StateConnection.ClientReceiveBuffer = new byte[10];
            //receiveDone.Reset();
            this.StateConnection.workSocket.BeginReceive(
                this.StateConnection.ClientReceiveBuffer, 
                0, 
                this.StateConnection.ClientReceiveBuffer.Length, 
                0,
                NewPacketHeader, 
                null);
            //receiveDone.WaitOne();
            // ^^^^^
        }
        else
        {
            // vvvvv Продолжаем читать предыдущий пакет
            this.StateConnection.LeftToReceiveLength = (this.StateConnection.ExpectedPacketLength) - this.StateConnection.ClientReceivedBytes;

            this.StateConnection.workSocket.BeginReceive(
                this.StateConnection.ClientReceiveBuffer,
                this.StateConnection.ClientReceivedBytes,
                this.StateConnection.LeftToReceiveLength,
                SocketFlags.None,
                EndRecieveQ0,
                null);
            // ^^^^^
        }
    }
    catch (Exception e)
    {
        if (e is SocketException)
        {
            //closeDone.Reset();
            termination(this.StateConnection.ID);
            //closeDone.WaitOne();
            return;
        }
    }
}
Scroll to Top