Используйте stringstream для чтения из сокета TCP - PullRequest
0 голосов
/ 30 ноября 2011

Я использую библиотеку сокетов (я бы не стал ее использовать), чьи операции recv работают с std::string, но являются просто оберткой для одного вызова функции сокета recv, так что, вероятно, что я получил только часть сообщения, которое хотел. Моим первым инстинктом было зацикливание и добавление полученной строки к другой строке, пока я не получу все, но это кажется неэффективным. Другой возможностью было сделать то же самое с массивом символов, но это кажется грязным. (Мне нужно проверить размер строк перед добавлением в массив, и если он переполнится, мне нужно где-то хранить строку, пока массив снова не станет пустым ..)

Так что я думал об использовании струнного потока. Я использую протокол TLV, поэтому мне нужно сначала извлечь два байта в unsigned short, затем получить определенное количество байтов из потока строк и затем повторять цикл, пока я не достигну поля разделителя.

Есть ли лучший способ сделать это? Я совершенно не на том пути? Есть ли лучшие практики? До сих пор я всегда видел только прямое использование библиотеки сокетов с массивами символов, поэтому мне любопытно, почему использование `std :: string`` со строковыми потоками может быть плохой идеей ..

Редактировать: Ответ на комментарий ниже: Библиотека - это библиотека, которую мы используем внутри, она не является публичной (хотя в ней нет ничего особенного, в основном это просто оболочка для библиотеки сокетов для добавления исключений и т. Д.).

Я должен упомянуть, что у меня есть рабочий прототип, использующий библиотеку сокетов напрямую.

Это работает примерно так:

int lengthFieldSize = sizeof(unsigned short);
int endOfBuffer= 0;//Pointer to last valid position in buffer.                                     
while(true) {
  char buffer[RCVBUFSIZE];
  while(true) {
    int offset= endOfBuffer;
    int rs= 0;
    rs= recv(sock, buffer+offset, sizeof(buffer)-offset, 0);
    endOfBuffer+= rs;
    if(rs < 1) {
      // Received nothing or error.                                                                         
      break;
    } else if(endOfBuffer == RCVBUFSIZE) {
      // Buffer full.                                                                               
      break;
    } else if(rs > 0 && endOfBuffer > 1) {
      unsigned short msglength= 0;
      memcpy((char *) &msglength, buffer+endOfBuffer-lengthFieldSize, lengthFieldSize);
      if(msglength == 0) {
        break; // Received a full transmission.                                                    
      }
    }
  }
  unsigned int startOfData = 0;
  unsigned short protosize= 0;
  while(true) {
    // Copy first two bytes into protosize (length field)                                          
    memcpy((char *) &protosize, buffer+startOfData, lengthFieldSize);
    // Is the last length field the delimiter?                                                     
    // Then reply and return. (We're done.)                                                        
    // Otherwise: Is the next message not completely in the buffer?                            
    // Then break. (Outer while will take us back to receiving)                                    
    if(protosize == 0) {
      // Done receiving. Now send:                                                                 
      SendReplyMsg(sock, lengthFieldSize);
      // Clean up.                                                                                 
      close(sock);
      return;
    } else if((endOfBuffer-lengthFieldSize-startOfData) < protosize) {
      memmove(buffer, buffer+startOfData, RCVBUFSIZE-startOfData);
      //Adjust endOfBuffer:                                                                        
      endOfBuffer-=startOfData;
      break;
    }
    startOfData+= lengthFieldSize;
    gtControl::gtMsg gtMessage;
    if(!gtMessage.ParseFromArray(buffer+startOfData, protosize)) {
      cerr << "Failed to parse gtMessage." << endl;
      close(sock);
      return;
    }
    // Move position pointer forward by one message (length+pbuf)                                  
    startOfData+= protosize;
    PrintGtMessage(&gtMessage);
  }
}

Так что в основном у меня есть большой цикл, который содержит цикл приема и цикл синтаксического анализа. Массив символов передается туда-сюда, так как я не могу быть уверен, что получил все, пока не проанализирую его. Я пытаюсь воспроизвести это поведение, используя «правильный» C ++ (т.е. std :: string)

1 Ответ

1 голос
/ 30 ноября 2011

Моим первым инстинктом было зацикливание и добавление полученной строки к другой строке, пока я не получу все, но это кажется неэффективным.

Конкатенация строк технически зависит от платформы, но, вероятно, для str1 + str2 потребуется одно динамическое размещение и две копии (из str1 и str2). Это довольно медленно, но гораздо быстрее, чем доступ к сети! Поэтому мой первый совет - следовать первому инстинкту и выяснить, достаточно ли он правильный и быстрый.

Если это не достаточно быстро, и ваш профилировщик показывает, что виноваты избыточные копии строк, рассмотрите возможность ведения списка строк (возможно, std::vector<string*>) и объединения всех строк один раз на конец. Это требует некоторой осторожности, но следует избегать избыточного копирования строк.

Но определенно сначала профиль!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...