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

时间:2012-02-27 04:00:40

标签: c# sockets tcp tcpclient tcplistener

我有一个问题,NetworkStream从套接字缓冲区中读取数据,不应该存在。顺便说一句,我发送了很大的缓冲区。现在我刚刚在localhost上进行测试。

这是我如何读取数据,前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();           
    }

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

server ------------- client

写作1024 - &gt;阅读1024

阅读1228800&lt; - 写作1228800

写作1024 - &gt;阅读1024

阅读1228800&lt; - 写作1228800

阅读7224842

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

为什么缓冲区会在其中获取额外数据?当我发送较小的信息时,一切似乎都运作良好,但这让我发疯了。

1 个答案:

答案 0 :(得分:6)

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

我们有完整的问题。您需要检查返回此内容的要求。不能保证(对于基于网络的IO,非常不可能),您将同时获得整个缓冲区 - 数据包即时进入,API将为您提供它可以。相反,你会得到“some”(结果> 0和&lt; = count)或“none”(结果&lt; = 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();
}