带有BeginReceive \ EndReceive的异步套接字意外数据包

时间:2014-03-21 14:03:42

标签: c# sockets asynchronous beginreceive

我需要收到异步消息。

在所有消息中,前2个字节表示下一个字节数组的长度。我的问题是,在少数情况下,我收到意外的数据包。

如果我使用Thread.Sleep(200),这个问题确实不会发生,或很少发生。

我哪里错了?

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];
    }
}

2 个答案:

答案 0 :(得分:2)

您假设TCP是基于消息的协议。但它是一个字节流。您的读数可以读取大于零的任何数量。这就像使用FileStream一样。文件也没有消息。

您的代码必须处理这个事实。搜索&#34; TCP消息框架&#34;。

答案 1 :(得分:0)

找到解决方案 很长一段时间后,我写了一个很好的解决方案,我希望能帮助别人。 非常感谢你的建议。

    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);
        }
    }