2012-02-27 2 views
3

У меня проблема с данными чтения NetworkStream из буфера сокета, которого не должно быть. Кстати, я отправляю очень большие буферы. Прямо сейчас я только что тестировал на локальном хосте.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 байта прочитанного.

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

+3

Метод NetworkStream Read возвращает 'Число байтов, считанных из NetworkStream.'. Вы игнорируете возвращаемое значение. Вы не можете предположить, что значение будет размером буфера. –

ответ

6
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(); 
} 
+0

Большое спасибо Марку, он работал отлично. Я потратил часы на отладку и не получил нигде. Прочитав сообщение, я исправил его через 2 минуты. – Rickyman35

Смежные вопросы