如何从 C++ 中的套接字中的线格式反序列化 int

Posted

技术标签:

【中文标题】如何从 C++ 中的套接字中的线格式反序列化 int【英文标题】:how to deserialize an int from wire format from socket in C++ 【发布时间】:2019-07-04 02:47:06 【问题描述】:

我有一个包含标题和正文的 protobuf 消息。标头有一个名为 size 的成员(反映整个消息的大小,即标头加正文)。由于 protobuf (AFAIK) 不提供消息边界,因此我使用大小(标头的第一个元素和 4 字节的固定大小)来了解从套接字中啜饮多少。

假设我已经从套接字读取(或 recv(2)ed) 4 个字节,我现在需要将它转换(解码)为 int,以便我可以指示 recv(2) 进行更多操作。

如何在 C++ 领域将此有线格式转换/解码为 int?

我试过 ParseFromString() 但这个函数返回一个布尔值,文档并没有告诉我太多。

我会展示我认为相关的内容......让我们看看

message PsdAgentMsg  
    message Header  
       fixed32 theSize = 1; // size includes header and message 
       uint32 theInstanceId = 2; 
       Type theMsgType = 3; 
     
    Header theHeader = 1;
    oneof theMsg  
       abc = 2; 
       def = 3; 
       ghi = 4; 
       klm = 5; 
       PsdAgentGPCMsg theGPCMsg = 6; 
     
 
message PsdAgentGPCMsg  
    int32 theCount = 1; 
 
...few minutes later..

PsdAgentMsg *msg = new PsdAgentMsg();   // psd msg is header + body
...
rc = recv(sock, buf, 4, 0);     // I am simplifying some stuff
std::string sizeString = buf;
...
size_t payloadSize = msg->ParseFromString(sizeString);

我发现payloadSize = 0。这是分配给size_t的错误。所以 ParseFromString() 似乎不是解码 4 个字节的正确方法。同样,我需要将 sizeString 解码为 int。所以我可以说 recv(sock, buf, payloadSize,0)

【问题讨论】:

发送的究竟是什么?它是带有单个 int32 字段的消息吗? payloadSize 的类型是什么?这个问题需要minimal reproducible example 才能回答。 对不起,我试图插入一些文字,但无法弄清楚。我尝试了单反引号,三次反引号,html标签......无济于事 那是错字...我已将其添加到原始帖子中 我们需要知道尺寸的线格式是什么。有很多可能的选择,如果不知道做出了哪个选择,就不可能编写代码。 【参考方案1】:

嵌入式消息字段使用线型 2:长度分隔。

这意味着它是这样编码的:

<varint tagAndType> <varint messageLength> <theSize> <theInstanceId> <theMsgType>

其中varint 是protobuf encoding specification 中定义的base-128 varint。


这意味着theSize 与消息开头的偏移量不是固定的。为了使事情更容易,您可以在发送消息本身之前发送消息的总大小。即给出这样的消息:

message PsdAgentMsg  
    message Header  
       uint32 theInstanceId = 2; 
       Type theMsgType = 3; 
    

    Header theHeader = 1;
    oneof theMsg  
       int32 abc = 2;
       string def = 3;
       double ghi = 4;
       uint32 klm = 5;
       PsdAgentGPCMsg theGPCMsg = 6; 
     

您可以像这样发送数据:

PsdAgentMsg msg;
fillOutMessageFields(msg);

std::string encoded_msg = msg.SerializeAsString();
uint32_t size = msg.size();

// First send the message size as a fixed-size integer
size = htonl(size);
send(sockfd, &size, sizeof(size), 0);

// Then send the message itself
send(sockfd, msg.c_str(), msg.size(), 0);

然后你可以像这样在另一端读回它:

// Read the size
uint32_t size;
rc = recv(sockfd, &size, sizeof(size), 0);
assert(rc == sizeof(size));  // Or proper error handling
size = ntohl(size);

// Read the actual message
std::string encoded_msg(size, '\0');
rc = recv(sockfd, &encoded_msg[0], size, 0);
assert(rc == size);  // Or proper error handling

// Parse the message
PsdAgentMsg msg;
msg.ParseFromString(encoded_msg);

【讨论】:

> 这意味着 theSize 与 msg 开头的偏移量不是固定的……我担心,我希望是这样。 嵌入消息字段的标签和类型和长度字段都是可变长度的。如果Header 中包含的所有类型都是固定大小的,那么您可以得出一个恒定的偏移量,但这取决于确切的消息定义。但由于uint32 是可变长度类型(我不知道Type 是什么),所以情况并非如此。最好只发送一个单独的固定长度大小的标头 IMO。 谢谢...我真的很想看看我是否可以避免两个电话的情况(一个电话说期望大小,另一个电话说,这是有效载荷)...所以让回到标题修改方法。你问什么类型是类型。 Type 是一个枚举,它似乎是一个 varint ,但我可以将其更改为 fixed32。另一个头字段(即InstanceID)当然可以是fixed32。然后您说“可以假设偏移量,但取决于消息”。您是说即使我将标头更改为所有 fixed32 元素,“theSize”偏移量仍然取决于消息?

以上是关于如何从 C++ 中的套接字中的线格式反序列化 int的主要内容,如果未能解决你的问题,请参考以下文章

c++中的序列化与反序列化怎么实现的?

如何通过 C++ 中的 boost 套接字发送 ostream?

如何序列化apache箭头c ++表,通过套接字传输,并在python端反序列化

使用 pybind11 从 C++ 反序列化 Python 中的 protobuf 缓冲区

如何从 OpenCV C++ 中的 HoughLines 函数输出中绘制所需的线?

C++ 中的序列化和 C# 中的反序列化,对于命名管道,反之亦然