Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过套接字发送数据时计算字节长度时出现问题_C#_Sockets - Fatal编程技术网

C# 通过套接字发送数据时计算字节长度时出现问题

C# 通过套接字发送数据时计算字节长度时出现问题,c#,sockets,C#,Sockets,我正在编写一个客户机/服务器应用程序。当使用环回地址连接时,它似乎工作得很好,但是当通过LAN或internet连接时,我遇到了一个我无法解决的非常奇怪的问题 服务器和客户端之间发送的所有数据都包含在一个名为“DataPacket”的类中。然后将该类序列化为二进制,以便可以使用套接字发送该类 在发送数据包之前,会附加一个4字节的报头,其中包含数据包的字节长度,以便接收方知道在对其进行反序列化之前需要多少字节。问题是,在某一点上,接收器认为它正在等待的数据比实际在报头中发送的数据多得多。有时这个数

我正在编写一个客户机/服务器应用程序。当使用环回地址连接时,它似乎工作得很好,但是当通过LAN或internet连接时,我遇到了一个我无法解决的非常奇怪的问题

服务器和客户端之间发送的所有数据都包含在一个名为“DataPacket”的类中。然后将该类序列化为二进制,以便可以使用套接字发送该类

在发送数据包之前,会附加一个4字节的报头,其中包含数据包的字节长度,以便接收方知道在对其进行反序列化之前需要多少字节。问题是,在某一点上,接收器认为它正在等待的数据比实际在报头中发送的数据多得多。有时这个数字是以百万字节为单位的,这造成了严重的问题

以下是数据的发送方式:

public override void Send(DataPacket packet)
{
    byte[] content = packet.Serialize();
    byte[] header = BitConverter.GetBytes(content.Length);

    List<Byte> bytes = new List<Byte>();
    bytes.AddRange(header);
    bytes.AddRange(content);

    if (socket.Connected)
    {
        socket.BeginSend(bytes.ToArray(), 0, bytes.Count, SocketFlags.None, OnSendPacket, socket);
    }
}
当从客户机接收到某些数据时,将调用EndReceive。如果StateObject.Data为空,则假定这是新数据包的第一部分,因此会检查报头。如果接收的字节数小于标头中显示的大小,则我再次调用BeginReceive,重新使用相同的StateObject

以下是OnReceiveData的代码:

private void OnReceiveData(IAsyncResult result)
{
    StateObject state = (StateObject)result.AsyncState;

    int bytes = state.Socket.EndReceive(result);
    if (bytes > 0)
    {
        state.ProcessReceivedData(bytes);

        if (state.CheckDataOutstanding())
        {
            //Wait until all data has been received.
            WaitForData(state);
        }
        else
        {
            ThreadPool.QueueUserWorkItem(OnPacketReceived, state);

            //Continue receiving data from client.
            WaitForData(new StateObject(state.Socket));
        }
    }
}
如上所述,我首先调用state.ProcessReceivedData()-这是我从数据中分离头,然后从缓冲区中移动数据的地方:

public void ProcessReceivedData(int byteCount)
{
    int offset = 0;

    if (Data.Count == 0)
    {
        //This is the first part of the message so get the header.
        System.Buffer.BlockCopy(Buffer, 0, Header, 0, HeaderSize);
        offset = HeaderSize;
    }

    byte[] temp = new byte[byteCount - offset];
    System.Buffer.BlockCopy(Buffer, offset, temp, 0, temp.Length);
    Data.AddRange(temp);
}
然后,我调用checkDataUnderstanding()将接收到的字节数与标头中的大小进行比较。如果这些匹配,则我可以安全地反序列化数据:

public bool CheckDataOutstanding()
{
    int totalBytes = BitConverter.ToInt32(Header, 0);
    int receivedBytes = Data.Count;
    int outstanding = totalBytes - receivedBytes;

    return outstanding > 0;
}
问题是报头中的值与发送时的值不同。这似乎是随机发生的,有时在服务器上,有时在客户端上。由于在网络上调试服务器和客户端的随机性和普遍的困难性,我发现这几乎是不可能的


我在这里遗漏了什么明显的或愚蠢的东西吗?

如果你只得到了标题的一部分,会发生什么?也就是说,假设您在第一次读取中得到了三个字节,而不是四个或更多?您的
ProcessReceivedData
函数需要考虑到这一点,在您至少有四个字节之前,不要尝试提取头大小。

虽然您是对的,这是我忽略的,并且肯定需要考虑的,但我不认为这是问题的原因。我刚刚尝试添加一个带有断点的小于4字节的签入ProcessReceivedData,但当问题发生时,它从未被击中。发布的代码只能对一个数据包正常工作。缺少重置StateObject(尤其是清空数据)所需的代码。足以导致所描述的问题。
public void ProcessReceivedData(int byteCount)
{
    int offset = 0;

    if (Data.Count == 0)
    {
        //This is the first part of the message so get the header.
        System.Buffer.BlockCopy(Buffer, 0, Header, 0, HeaderSize);
        offset = HeaderSize;
    }

    byte[] temp = new byte[byteCount - offset];
    System.Buffer.BlockCopy(Buffer, offset, temp, 0, temp.Length);
    Data.AddRange(temp);
}
public bool CheckDataOutstanding()
{
    int totalBytes = BitConverter.ToInt32(Header, 0);
    int receivedBytes = Data.Count;
    int outstanding = totalBytes - receivedBytes;

    return outstanding > 0;
}