【问题标题】:Problems parsing a TCP-based TLV protocol in C++在 C++ 中解析基于 TCP 的 TLV 协议时出现问题
【发布时间】:2011-11-01 00:57:21
【问题描述】:

我正在尝试通过 TCP 实现 (T)LV 协议,从 python 客户端发送协议缓冲区并使用 C++ 服务器接收。

我的代码或多或少是这样的:

char* buffer[RCVBUFSIZE];
int recvsize= 0;
// Filling my buffer with as much as possible.
while(true) {
  if(recvsize == RCVBUFSIZE) {
    break;
  } else if(recvsize+= recv(sock, buffer+recvsize, sizeof(buffer)-recvsize, 0) < 1) {
    break;
  }
}
//Parsing LV protocol
while(true) {
  unsigned short protosize= 0;
  //Copy first two bytes into protosize
  memcpy((char *) &protosize, buffer, sizeof(unsigned short));
  if(protosize == 0) { break; } // Protocol indicates EOM be setting length to "0"
  void* protomsg[protosize];
  memcpy(protomsg, buffer+sizeof(unsigned short), protosize);
  int msglength= sizeof(unsigned short)+protosize;
  //Now I'll move the whole buffer to the left so that I don't have to keep track of where I'm at.
  memmove(buffer, buffer+msglength, RCVBUFSIZE-msglength);
  protoMsg pm;
  if(!pm.ParseFromArray(protomsg, protosize)) { break; } // Parsing failed.
  // Do stuff with parsed message.
}

现在我有几个问题:

  • 接收消息的while 循环永远不会终止。我怀疑当没有任何数据时,recv 调用会阻塞,而我预计它会返回错误。我找到了 select 函数来检查是否有要读的东西。我会尝试一下。 但是当我只调用一次来跳过这个问题时(收到的消息大约 10 个字节,所以我希望在一次调用中收集所有内容。)我遇到了另一个问题:
  • memcpy 和 memmove 似乎没有按预期工作。在第一个循环中,short 被按预期处理(我收到了与我在另一端发送的相同的值),但随后解析协议缓冲区的所有内容都失败了。我是不是误会了什么?

编辑:关于 ntohs 的评论——我目前正在以 little-endian 的形式传输短片,忘记提及了。 (顺便说一句,我仍然会改变它。)

第三次编辑:代码现在可以工作了,但我必须更改以下内容:

char* buffer[RCVBUFSIZE];
int recvsize= 0;
// Filling my buffer with as much as possible.
while(true) {
  if(recvsize == RCVBUFSIZE) {
    break;
  } else if((recvsize+= recv(sock, buffer+recvsize, sizeof(buffer)-recvsize, 0)) < 1) {
    break;
  } else if(recvsize > 1) {
    unsigned short msglength= 0;
    memcpy((char *) &msglength, buffer+recvsize-sizeof(unsigned short), sizeof(unsigned short));
    if(msglength == 0) { break; } // Received a full transmission.
  }
}

所以首先我需要在recvsize+= recv() 语句周围添加括号,然后因为非阻塞方法由于某种原因不起作用,我现在正在检查传输的最后两个字节在读取时是否转换为 0无符号短。如果我偶然读到一个不是长度字段的 0,这可能会导致问题。我可能会就此提出另一个问题。

我也将protomsg 更改为char[],但我认为这并没有真正改变任何东西。 (我已经解析了一个 void 数组..)

【问题讨论】:

  • 您应该在收到的short 上调用ntohs(并在发送时调用htons -“网络到主机,短”/“主机到网络,短”)

标签: c++ linux tcp


【解决方案1】:

如果您收到的消息总是大约 10 个字节,而RCVBUFSIZE 不止于此,您将永远不会终止,直到出现读取数据错误为止。此外,代码中的buffer 变量是RCVBUFSIZE 指针数组,可能不是您想要的。

修改你的代码如下:

#define MINIMALMESSAGESIZE 10  // Or what the size may be
char buffer[RCVBUFSIZE];
int totalrecvsize= 0;
int recvsize= 0;
while(true) {
  if(totalrecvsize >= MINIMALMESSAGESIZE) {
    break;
  } else if(recvsize= recv(sock, buffer+totalrecvsize, sizeof(buffer)-totalrecvsize, 0) < 1) {
    break;
  } else {
    totalrecvsize += recvsize;
  }
}

【讨论】:

  • 啊,对了,我在从我的来源转录时忘记了“+=”,它现在在我原来的问题中。我不确定如何最小化消息大小,因为我只能通过解析长度字段来解决这个问题。这样做意味着对 recv 的额外调用,我宁愿避免。
  • 嗯,好的,请阅读您的编辑。问题是我目前只传输一个大约 10 个字节的协议缓冲区。部署时,应该传输从 1 到 1000 的所有内容,我希望尽快从线路中获取尽可能多的内容。
  • @dinyar:不要忘记在对recv 的调用中将接收到的总大小添加到缓冲区中,否则您将覆盖您已经收到的内容。另外,如果您不知道消息大小,那么您可以在调用recv 时一直等待,除非套接字它当然是非阻塞的,在这种情况下会返回错误。
  • 是的,我想我正在使用recvsize+= recv 这样做。我会考虑让套接字不阻塞,谢谢你的提示!
  • @dinyar:现在看到你的第二条评论了。在这种情况下,您只需要注意不要在接收循环中覆盖缓冲区。 :)
猜你喜欢
  • 2011-12-17
  • 2011-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-10
  • 1970-01-01
相关资源
最近更新 更多