【问题标题】:Error in recieving data from TCP Client从 TCP 客户端接收数据时出错
【发布时间】:2012-10-14 18:47:31
【问题描述】:

我编写了一个应用程序,它监听一个端口并接收一些数据包,根据我的自定义协议,数据包是 49 字节到 1500 字节,我可以从数据包中的数据长度看出。我应该解释和处理 49 字节数据包和更大数据包中的数据的方式是不同的。

问题是,当我收到小于 1374 字节的数据包时,一切正常,但是当数据包长度变长时,我收到以下异常,并且我还丢失了数据的最后 4 个字节(我用 1384 字节测试过数据包,我丢失了最后 4 个字节)

引发的异常: 指数超出范围。必须是非负数且小于集合的大小。 参数名称:startIndex

每个 49 字节的数据包有 35 字节的数据,更大的数据包的数据长度是不确定的(因为压缩)。

我发现有时最后 4 个字节位于单独的“字节”和“结果”变量中,这意味着它们被视为新数据包,而不是附加到它们所属的数据包。

这是接收数据的代码:

TcpClient Client = obj as TcpClient;
        EndPoint ep = Client.Client.RemoteEndPoint;
        List<Byte> result = new List<byte>();
        result.Capacity = 2000;
        try
        {


            NetworkStream stream = Client.GetStream();
            int i = 49;
            while ((i = stream.Read(bytes, 0,49)) != 0)
            {

                for (int id = 0; id < i; id++)
                {
                    result.Add(bytes[id]);
                }
//reading data length to determine packet length
                byte[] tmp = new byte[2];
                tmp = BitConverter.GetBytes(BitConverter.ToUInt16(result.ToArray(), 9));
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(tmp);
                }
                Int16 l = BitConverter.ToInt16(tmp, 0);
                if (l>35)
                {
                    stream.Read(bytes, result.Count, l - 35);
                    for (int id = 49; id <((l-35)+49); id++)
                    {
                        result.Add(bytes[id]);
                    }
                    if (this.TCPDataReceivedHandler != null)
                    {
                        this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
                        result.Clear();
                        Array.Clear(bytes, 0, 2000);
                        result.Capacity = 2000;
                    }
                }
                else
                {
                    if (this.TCPDataReceivedHandler != null)
                    {
                        this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
                        result.Clear();
                        Array.Clear(bytes, 0, 2000);
                        result.Capacity = 2000;
                    }
                }


            }
            System.Diagnostics.Debug.WriteLine("client Close");
            Client.Close();

        }
        catch (System.Exception ex)
        {
            throw ex;
        }
        finally
        {
            Client.Close();
            this.clients.Remove(Client);
        }

根据格雷格的建议和我的研究,我也尝试使用以下方法:

NetworkStream stream = Client.GetStream();
            int bytesread = 0, OffsetTemp = 0;

            while (stream.CanRead)
            {
                OffsetTemp = 0;

                bytesread += stream.Read(bytess, OffsetTemp, 11);
                OffsetTemp = OffsetTemp + 11;



                byte[] tmp = new byte[2];
                tmp = BitConverter.GetBytes(BitConverter.ToUInt16(bytess.ToArray(), 9));
                if (BitConverter.IsLittleEndian)
                {
                    Array.Reverse(tmp);
                }
                Int16 l = BitConverter.ToInt16(tmp, 0);

                bytesread += stream.Read(bytess, OffsetTemp++, 11 + l + 3);
                for (int id = 0; id < l + 14; id++)
                {
                    result.Add(bytess[id]);
                }

                if (this.TCPDataReceivedHandler != null)
                {
                    this.TCPDataReceivedHandler(ep, result.Count, result.ToArray());
                    result.Clear();
                    Array.Clear(bytess, 0, 2000);
                }

            }

【问题讨论】:

    标签: c# sockets tcpclient tcplistener tcp-ip


    【解决方案1】:

    使用 TCP 时,您必须记住,您放入的数据块的大小不保证与您在接收端输出的数据块的大小相同。 TCP 是一种流协议,因此它保证相同的字节以与发送时相同的顺序到达另一端(如果不是,则套接字连接将被重置)。 TCP 在对send() 的调用之间不维护任何类型的块边界,并且块可以根据网络条件任意拆分或合并。

    您的接收者必须准备好处理接收来自对stream.Read() 的调用的任何数据量,但您的代码似乎没有这样做。例如,即使stream.Read() 一次只接收 一个 字节,正确编写的接收器代码也应该继续正常工作。

    【讨论】:

    • 感谢 Greg,但是如果我继续从 stream.Read() 读取数据包而不注意数据包中的数据长度字段,我如何确保数据包不会相互混合?数据包在短时间内连续发送。
    • 您的协议似乎有一个标头,指定了以下数据块的长度。所以只读取当前块的内容,然后循环到顶部并读取下一个块的长度标题。
    • 亲爱的 Greg,我尝试使用你的建议但没有奏效,我仍然无法接收超过 1380 字节的数据。
    • 您已经更改了代码,但仍然存在同样的问题。在每次调用stream.Read() 时,您必须处理收到的字节数少于请求的字节数的情况。当您请求 11 个字节时,您可能会收到 1 到 11 个字节之间的任何值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 2016-12-06
    • 2021-09-30
    相关资源
    最近更新 更多