StefanoM5
StefanoM5

Reputation: 1337

Asynchronous socket unexpected packet with BeginReceive \ EndReceive

I need to receive an async message.

In all messages, first 2 byte indicate the length of next byte array. My problem is that in few case I receive unexpected packets.

If I use Thread.Sleep(200) this problems does't happen, or happens rarely.

Where am I wrong?

protected void StartListening()
{
    StateObject state = new StateObject() { ProcessHeader = true };
    state.PrepareBuffer(HeaderLength);
    lock (_secureConnection)
        _secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
}

private void ReadCallback(IAsyncResult ar)
{
    if (_disposing)
        return;
    StateObject state = (StateObject)ar.AsyncState;
    try
    {
        lock (_secureConnection)
            _secureConnection.EndReceive(ar);
        if (state.ProcessHeader)
        {
            state.ProcessHeader = !state.ProcessHeader;
            var bodyLength = GetBodyLength(state.Buffer);
            //Thread.Sleep(200);
            state.CompleteMessage.AddRange(state.Buffer);
            state.PrepareBuffer(bodyLength);
            lock (_secureConnection)
                _secureConnection.BeginReceive(state.Buffer, 0, bodyLength, 0, new AsyncCallback(ReadCallback), state);
        }
        else
        {
            state.CompleteMessage.AddRange(state.Buffer);
            ProcessMessage(state.CompleteMessage); //process this message
            //Thread.Sleep(200);
            state.ProcessHeader = !state.ProcessHeader;
            state.CompleteMessage.Clear();
            state.PrepareBuffer(HeaderLength);
            lock (_secureConnection)
                _secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
        }
    }
    catch (Exception e)
    {
        Close(true);
    }
}

class StateObject
{
    public StateObject()
    {
        ProcessHeader = true;
    }
    public byte[] Buffer { get; private set; }
    public bool ProcessHeader { get; set; }
    public List<byte> CompleteMessage = new List<byte>();
    public void PrepareBuffer(int size)
    {
        Buffer = new byte[size];
    }
}

Upvotes: 1

Views: 510

Answers (2)

StefanoM5
StefanoM5

Reputation: 1337

SOLUTION FOUND After a long time, i've written a good solution, I hope to help someone. Thank you very much for your suggestions.

    int HeaderLength = 2;
    int bodyLength;
    int bytesReceived;
    int totalBytesReceived;

    protected void StartListening()
    {
        StateObject state = new StateObject() { ProcessHeader = true };
        state.PrepareBuffer(HeaderLength);
        lock (_secureConnection)
            _secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
    }

    /// <summary>
    /// Reads the callback.
    /// 
    /// A message is in this form:
    /// 
    /// 2 bytes indicate the leght of body message (n)
    /// n bytes for body message
    /// 
    /// </summary>
    /// <param name="ar">IAsyncResult.</param>
    private void ReadCallback(IAsyncResult ar)
    {
        if (_disposing)
            return;
        StateObject state = (StateObject)ar.AsyncState;
        try
        {
            lock (_secureConnection)
                bytesReceived = _secureConnection.EndReceive(ar);

            if (state.ProcessHeader)    //In this phase I receive 2 bytes that indicate the total length of the next message
            {
                state.ProcessHeader = !state.ProcessHeader;
                bodyLength = GetBodyLength(state.Buffer);   //I interpret 2 bytes to know body message length
                state.CompleteMessage.AddRange(state.Buffer);
                state.PrepareBuffer(bodyLength);
                totalBytesReceived = bytesReceived = 0;
                lock (_secureConnection)
                    _secureConnection.BeginReceive(state.Buffer, 0, bodyLength, 0, new AsyncCallback(ReadCallback), state);
            }
            else    //In this phase I receive the message, with one or more recursive CallBack
            {
                state.CompleteMessage.AddRange(state.Buffer.ToList().GetRange(0, bytesReceived));

                totalBytesReceived += bytesReceived;
                int totalBytesMissing = bodyLength - totalBytesReceived;


                if (totalBytesReceived < bodyLength)
                {
                    state.PrepareBuffer(totalBytesMissing);
                    lock (_secureConnection)
                        _secureConnection.BeginReceive(state.Buffer, 0, totalBytesMissing, 0, new AsyncCallback(ReadCallback), state);
                    return;
                }
                //totalMessageLenght = body length plus first 2 bytes indicate body length
                int totalMessageLenght = bodyLength + 2;    

                var completeMessage = state.CompleteMessage.GetRange(0, totalMessageLenght).ToList();
                ProcessMessage(completeMessage);
                state.ProcessHeader = !state.ProcessHeader; //I prepare Callback to read 2 bytes indicate the total length of the next message

                state.CompleteMessage.Clear();
                state.PrepareBuffer(HeaderLength);
                lock (_secureConnection)
                    _secureConnection.BeginReceive(state.Buffer, 0, HeaderLength, 0, new AsyncCallback(ReadCallback), state);
            }
        }
        catch (Exception e)
        {
            Close(true);
        }
    }

Upvotes: 0

usr
usr

Reputation: 171178

You are assuming that TCP is a message-based protocol. It is a stream of bytes, though. Your reads can read any amount greater than zero. This is just like with a FileStream. Files do not have messages, either.

Your code has to deal with that fact. Search for "TCP message framing".

Upvotes: 2

Related Questions