NetworkStream正在读取不应该存在的数据

本文关键字:存在 数据 不应该 读取 NetworkStream | 更新日期: 2023-09-27 18:25:57

我在NetworkStream从套接字缓冲区读取数据时遇到问题,该缓冲区不应该存在。顺便说一下,我正在发送非常大的缓冲区。现在我刚刚在本地主机上进行了测试。

以下是我读取数据的方式,前4个字节包含消息的长度,然后我只读取4096个块,直到它达到消息的长度。

    protected TcpClient tcpObject;
    protected NetworkStream tcpStream;
    private void HandleComm()
    {
        try
        {
            tcpStream = tcpObject.GetStream();
            byte[] totalByteAray = new byte[constIntSize];
            byte[] message = new byte[constChunkSize];
            byte[] fullMessage = new byte[0];
            //this is how many bytes long the message will be
            int totalBytes = 0;
            int currentBytes = 0;
            int chunkSize = constChunkSize;
            while (true)
            {
                //skip reading if no data is available
                //DataAvailable does not tell you when all the data has arrived
                //it just tell you if some data has arrived
                if (tcpStream.CanRead)
                {
                    totalBytes = 0;
                    currentBytes = 0;
                    message = new byte[constChunkSize];
                    chunkSize = constChunkSize;
                    //The first 4 bytes of the message will always contain the length of the message, not including
                    //the first 4 bytes. This is how you know when to stop reading.
                    tcpStream.Read(totalByteAray, 0, constIntSize);                        
                    //there are 4 bytes in a 32 bit number, so totalByteArrayContains 4 index that is a byte which is
                    //the 32 bit int that tells us how many bytes the whole message will be.
                    //now convert the totalByteArray to a 32bit int
                    totalBytes = BitConverter.ToInt32(totalByteAray, 0);
                    Console.WriteLine("reading " + totalBytes);
                    //fullMessage will contain the entire message but it has to be built message by message.                    
                    fullMessage = new byte[totalBytes];
                    //keep reading until we get all the data
                    while (currentBytes < totalBytes)
                    {
                        //when you send something over TCP it will some times get split up
                        //this is why you only read in chuncks, 4096 is a safe amount of bytes
                        //to split the data into.
                        if (totalBytes - currentBytes < constChunkSize)
                        {
                            chunkSize = totalBytes - currentBytes;
                            message = new byte[chunkSize];
                        }
                        tcpStream.Read(message, 0, chunkSize);
                        //since we know each chunk will always come in at 4096 bytes if it doesn't that means that it's the end
                        //this part cuts off the extra empty bytes                           
                        //copy the message to fullMessage starting at current bytes and ending with the bytes left
                        message.CopyTo(fullMessage, currentBytes);
                        currentBytes += chunkSize;                            
                    }
                    //message has successfully been received
                    if (totalBytes != 0)
                    {
                        if (OnRawDataReceived != null)
                        {
                            RawDataReceivedArgs args = new RawDataReceivedArgs();
                            args.Data = new byte[fullMessage.Length];
                            fullMessage.CopyTo(args.Data, 0);
                            OnRawDataReceived(this, args);
                        }
                        totalBytes = 0;
                    }
                }
            }
        }
        catch
        {
            connectionStatus = ConnectionStatus.NotConnected;
            if (OnDisConnect != null)
                OnDisConnect(this, null);
        }
    }

以下是我发送数据的方式,我只需要得到消息的长度,然后创建一个新消息,前4个字节是消息的长度而其余的是实际消息。

    protected void sendData(byte[] data)
    {
        //we need to know how big the data that we are sending will be
        int length = data.Length;
        System.Console.WriteLine("writing " + length);
        //convert the 32bit int to a 4 byte array
        byte[] lengthArray = BitConverter.GetBytes(length);
        //init the main byte array that will be sent over
        byte[] buffer = new byte[length + constIntSize];
        //the first 4 bytes will contain the length of the data
        lengthArray.CopyTo(buffer, 0);
        //the rest of the buffer will contain the data being sent
        data.CopyTo(buffer, constIntSize);
        //wite it to the client stream
        tcpStream.Write(buffer, 0, length + constIntSize);
        //now send it
        tcpStream.Flush();           
    }

由于某种原因,我正在读取不应该在缓冲区上的数据。这是控制台输出。

服务器---------------客户端

写入1024->读取1024

读数1228800<-写入1228800

写入1024->读取1024

读数1228800<-写入1228800

读取7224842

所以当我点击一个按钮时,它会发送一个请求,说我想要一个来自网络相机的图像,请求是1024字节。客户端读取它并发送1228800字节的图像。我第一次这样做的时候,它总是有效的。我第二次点击它时,客户端发回了1228800个字节,服务器读取了正确的字节数,然后在套接字缓冲区应该为空时找到了更多的字节来读取。我在套接字缓冲区中没有7224842个字节,这正是读取的前4个字节所说的。

你知道为什么缓冲区中会有额外的数据吗?当我发送较小的消息时,一切似乎都很好,但这让我抓狂。

NetworkStream正在读取不应该存在的数据

tcpStream.Read(totalByteAray, 0, constIntSize);
...
tcpStream.Read(message, 0, chunkSize);

我们有整个问题。要求检查对此的返回。不能保证(对于基于网络的IO,几乎不可能)您将同时获得整个缓冲区-数据包随时间传入,API将为您提供它所能提供的。相反,您将得到"some"(结果>0且<=count)或"none"(结论<=0)。

如果您想读取精确那么多数据,那么编写一个实用方法:

static void ReadExact(Stream stream, byte[] buffer, int offset, int count)
{
    int read;
    while(count > 0 && (read = stream.Read(buffer, offset, count)) > 0) {
        offset += read;
        count -= read;
    }
    if(count != 0) throw new EndOfStreamException();
}