NetworkStream читает данные, которых там быть не должно - PullRequest
2 голосов
/ 27 февраля 2012

У меня проблема с чтением данных 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 </p>

написание 1024 -> чтение 1024

чтение 1228800 <- запись 1228800 </p>

чтение 7224842

Поэтому, когда я нажимаю кнопку, она отправляет запрос о том, что я хочу получить изображение с веб-камеры, запрос состоит из 1024 байтов. Клиент читает его и отправляет изображение размером 1228800 байт. В первый раз, когда я делаю это, это всегда работает. Во второй раз, когда я щелкнул по нему, клиент отправил обратно 1228800 байт, сервер прочитал правильное количество байтов, а затем нашел больше байтов для чтения, когда буфер сокета должен был быть пустым. У меня не было 7224842 байта в буфере сокета, это то, что сказали первые 4 байта чтения.

Есть идеи, почему в буфер поступают дополнительные данные? Кажется, что все работает хорошо, когда я отправляю небольшие сообщения, но это сводит меня с ума.

1 Ответ

6 голосов
/ 27 февраля 2012
tcpStream.Read(totalByteAray, 0, constIntSize);
...
tcpStream.Read(message, 0, chunkSize);

и там у нас вся проблема.Это требование , что вы проверяете возврат к этому.Не гарантируется (и для сетевого ввода-вывода, весьма маловероятно), что вы получите весь буфер сразу: пакеты поступают как и когда, и API даст вам , что он может ,Скорее вы получите «некоторые» (результат> 0 и <= количество) или «ничего» (результат <= 0). </p>

Если вы хотите прочитать точно столько данных,затем напишите служебный метод:

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();
}
...