使用stringstream从TCP套接字读取

时间:2011-11-30 17:41:26

标签: c++ tcp

我正在使用一个套接字库(我宁愿不使用它),recv操作与std::string一起使用,但它只是recv套接字调用的包装器功能,所以我可能只得到了我想要的部分信息。我的第一直觉是进入一个循环并将收到的字符串附加到另一个字符串,直到我得到所有内容,但这似乎效率低下。另一种可能性是使用char数组做同样的事情,但这看起来很混乱。 (我必须在添加到数组之前检查字符串大小,如果它溢出,我需要将字符串存储在某处,直到数组再次为空。)

所以我在考虑使用stringstream。我使用TLV协议,所以我需要首先将两个字节提取到unsigned short中,然后从stringstream获取一定数量的字节,然后再循环直到我到达分隔符字段。

有没有更好的方法呢?我完全走错了路吗?有没有最佳做法?到目前为止,我一直只看到直接使用带有char数组的套接字库,所以我很好奇为什么在字符串流中使用`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 个答案:

答案 0 :(得分:1)

  

我的第一直觉是进入循环并将收到的字符串追加到另一个字符串,直到我得到所有内容,但这似乎效率低下。

字符串连接在技术上取决于平台,但可能str1 + str2需要一个动态分配和两个副本(来自str1str2)。这有点慢,但它比网络访问快得多!因此,我的第一条建议就是以你的第一直觉,去了解它是否正确和足够快。

如果它足够快,并且您的探查器显示冗余字符串副本应该受到指责,请考虑维护一个字符串列表(可能是std::vector<string*>)并将所有字符串连接在一起一旦到了最后。这需要一些小心,但应避免一堆冗余的字符串复制。

但绝对要先发布!