套接字开始接收回调意外结果

时间:2013-08-09 07:08:15

标签: c# .net sockets

在下面的代码中,状态是一个具有msgLength属性的对象,它是消息的总长度.msgLength是消息的前四个字节。 当在“快速”连续中收到多条消息时,我在state.msgLength属性中得到意外结果。当state.msglength不符合预期时 我试图收到错误。指定的参数超出了有效值的范围。

              StateObj state = (StateObj)ar.AsyncState;

            try
            {
                state.read += state.socket.EndReceive(ar);
                if (state.read == 0)
                {
                    state.socket.Close();
                    state.socket.Dispose();
                    state.socket = null;
                    this.Invoke((MethodInvoker)delegate { listBox1.Items.Add(DateTime.Now.ToString("HH:mm ss") + " socket closed"); });
                    return;
                }
            }
            catch (Exception)
            {
                this.Invoke((MethodInvoker)delegate { listBox1.Items.Add(DateTime.Now.ToString("HH:mm ss") + " socket closed"); });
                return;
            }
            if (state.read < 4)
            {
                //read again you dont have state,msglength
                state.socket.BeginReceive(state.dataBuffer, state.read, 4 - state.read, 0, cbReceive, state);
            }
            else
            {
                state.msglength = BitConverter.ToInt32(state.dataBuffer, 0);
            }
            if (state.read < state.msglength)
            {
                //read again you dont have the complete message 
                //here is the error Specified argument was out of the range of valid values. 
                state.socket.BeginReceive(state.dataBuffer, state.read, state.msglength - state.read, 0, cbReceive, state);
            }
            else
            {
            //process here
             if (state.read > state.msglength )
                {
                    state.read = state.read - state.msglength;
                }
                else
                {
                    state.read = 0;
                }
                state.socket.BeginReceive(state.dataBuffer, 0, state.dataBuffer.Length, 0, cbReceive, state);
            }

1 个答案:

答案 0 :(得分:0)

我认为 state.socket.BeginReceive 执行两次。 因为当它正在读取它的标题时, if(state.read&lt; 4) if(state.read&lt; state.msglength)都为真。在第一个 state.socket.BeginReceive之后返回( ..可以解决问题。

if (state.read < 4)
        {
            //read again you dont have state,msglength
            state.socket.BeginReceive(state.dataBuffer, state.read, 4 - state.read, 0, cbReceive, state);
           return;  //  <------
        }
        else
        {
            state.msglength = BitConverter.ToInt32(state.dataBuffer, 0);
        }
        if (state.read < state.msglength)
        {
            //read again you dont have the complete message 
            //here is the error Specified argument was out of the range of valid values. 
            state.socket.BeginReceive(state.dataBuffer, state.read, state.msglength - state.read, 0, cbReceive, state);
        }
        else
        {
           state.read = 0;
          //process correctly expected message
        }

我想我发现了另一个问题,如果消息中指定的messageLength只是数据的大小。您应该阅读 if(state.read&lt; state.msglength + 4)因为长度标头包含在您的缓冲区中。

我主要是分割阅读标题/数据。喜欢:(PSEUDO)

byte[] header;
byte[] buffer;
int bytesRead;

public void ReadHeader()
{
    bytesNeeded = 4;
    bytesRead = 0;
    BeginReadHeader();
}

public void BeginReadHeader()
{
    BeginReceive(header, bytesRead, bytesNeeded-bytesRead, EndReadHeader);
}
public void EndReadHeader()
{
    int read = EndReceive();

    if(read == 0)
    {
        CloseSocket();
        return;
    }

    bytesRead += read;

    if(bytesRead == byteNeeded)
        ReadData();
    else
        BeginReadHeader();
}

public void ReadData()
{
    bytesNeeded = BitConverter.ToInt32(header, 0);
    bytesRead = 0;
    if(buffer.Length < bytesNeeded)
        buffer = new byte[bytesNeeded];

    BeginReadData();
}

public void BeginReadData()
{
    BeginReceive(buffer, bytesRead, bytesNeeded-bytesRead, EndReadData);
}

public void EndReadData()
{
    int read = EndReceive();

    if(read == 0)
    {
        CloseSocket();
    }

    bytesRead += read;

    if(bytesRead == byteNeeded)
    {
        HandleData();
    }
    else
        BeginReadData();
}

public void HandleData()
{
     // handle data in buffer. BinaryReader

     ReadHeader();
}