在c++中解析基于tcp的TLV协议的问题

Problems parsing a TCP-based TLV protocol in C++

本文关键字:tcp TLV 协议 问题 c++      更新时间:2023-10-16

我正在尝试在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调用阻塞,而我期望它返回一个错误。我已经找到了选择函数来检查是否有东西要读。我会试试的。但是当我只调用一次receive来跳过这个问题时(接收到的消息大约有10个字节,所以我希望在一次调用中收集所有的消息)。我有另一个问题:
  • memcpy和memmove似乎没有按预期工作。在第一个循环中,short按预期处理(我在另一端接收到与发送相同的值),但随后协议缓冲区解析的所有内容都失败了。我误解了什么吗?

Edit:关于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数组…)

如果您收到的消息总是在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;
  }
}