使用stringstream从TCP套接字读取

Use stringstream to read from TCP socket

本文关键字:套接字 读取 TCP stringstream 使用      更新时间:2023-10-16

我正在使用一个套接字库(我宁愿不使用它),其recv操作与std::string一起工作,但只是recv套接字函数调用的包装器,所以很可能我只得到了我想要的消息的一部分。我的第一反应是进入一个循环,并将接收到的字符串附加到另一个字符串,直到得到所有内容,但这似乎效率低下。另一种可能是对字符数组执行相同的操作,但这似乎很混乱。(在添加到数组之前,我必须检查字符串的大小,如果它溢出,我需要将字符串存储在某个地方,直到数组再次为空…)

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

有更好的方法吗?我是否完全走错了方向?有什么最佳实践吗?到目前为止,我一直只看到直接使用套接字库与字符数组,所以我很好奇为什么使用' std::string ' '与stringstreams可能是一个坏主意。

编辑:回复下面的评论:这个库是我们内部使用的,它不是公共的(虽然它没有什么特别的,主要只是套接字库的包装器,以添加异常等)。

我应该提到我有一个直接使用套接字库的工作原型。

工作原理如下:

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)

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

字符串连接在技术上是平台相关的,但str1 + str2可能需要一个动态分配和两个副本(来自str1str2)。这有点慢,但比网络访问快得多!因此,我的第一个建议是,跟着你的第一直觉走,看看它是否既正确又足够快。

如果不够快,并且您的分析器显示多余的字符串副本是罪魁祸首,请考虑维护一个字符串列表(也许是std::vector<string*>),并在最后将所有字符串连接在一起。这需要一些小心,但应该避免一堆冗余的字符串复制。

但绝对是配置文件第一!