如何从 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++ 中的 boost 套接字发送 ostream?
如何序列化apache箭头c ++表,通过套接字传输,并在python端反序列化
使用 pybind11 从 C++ 反序列化 Python 中的 protobuf 缓冲区