协议缓冲区缺少必填字段,但已填写

Posted

技术标签:

【中文标题】协议缓冲区缺少必填字段,但已填写【英文标题】:ProtocolBuffer missing required fields but they are filled in 【发布时间】:2014-10-21 10:24:53 【问题描述】:

我正在创建一个客户端-服务器环境,并且我刚刚实现了 UDP。我之前只使用 TCP 连接进行工作和测试,然后一切正常。 现在,如果我尝试从客户端向服务器发送数据包,我会收到一个错误,即某些必填字段未填写,它们显然是(请参阅稍后的代码)。我真的不知道为什么会这样,因为它以前有效。

我猜可能很重要的一件事是,TCP 套接字和 UDP 套接字的接收方法在不同的线程上。 (服务器使用多线程)

所以,这里有一些代码:

TCP 接收(部分):

iResult = recv(m_Connections[t].socket, m_TCPRecvbuf, m_TCPRecvbuflen, 0);

// Deserialize the data
MessageID* receivedData = new MessageID();
receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());

我最初使用 iResult 在 ParseFromArray 中设置了大小(这似乎可行),但我更改了它,因此我将大小放入数据本身中。 (现在变成了receivedData->packetsize())

不同线程上的 UDP 接收(部分):

iResult = recvfrom(m_UDPListenSocket, m_UDPRecvbuf, m_UDPRecvbuflen, 0, (struct sockaddr *)&address, &addrlen);
// Deserialize the data
MessageID* receivedData = new MessageID();
receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());

这是第一次创建发送的数据包:

void NetworkInterface::SendLoginData(string username, string password)

// Create base message
// -----------------
MessageID message;
message.set_type(MessageID::Type::MessageID_Type_LOGINDATA);

// Create logindata
// ------------------
LoginData data = message.logindata();
data.set_username(username);
data.set_password(password);

// Create packet
// ----------------------
int size = message.ByteSize();
void* buffer = malloc(size);

message.set_packetsize(size);
message.set_clientid(0);

message.SerializeToArray(buffer, size);

if (m_pTCPNetworkingObject != nullptr)
    m_pTCPNetworkingObject->SendData(buffer, size);
else
    printf("NetworkInterface [ERROR]: No connection data was found. Please use CreateConnectionObject() first!\n");

在此之后它给出的错误是缺少 Type、ClientID 和 packetSize 字段,但您可以看到这些字段已填写

这里是 SendData() 方法:

void SendData(void* data, int size)

    // SEND DATA
    int iResult;
    iResult = send(m_TCPConnectSocket, (char*)data, size, 0 );
    if (iResult == SOCKET_ERROR) 
        printf("send failed with error: %d\n", WSAGetLastError());

这是我能提供的所有信息。任何帮助表示赞赏。

【问题讨论】:

你打电话给receivedData->packetsize() 有一条新消息——这是故意的吗?你不应该使用iResult吗?您确定可以在单个 UDP 数据包中传输整个消息吗? 你到底在哪里得到错误?是编译错误还是运行时出错? @JonSkeet 这是真的。我还没有检查过UDP。嗯,是的,packetsize 的东西......我应该检查一下 o_weisman 它是在运行时 @Dries:“首先读取数据包的第一部分”是什么意思?您是否调试过对 packetsize() 的调用返回的内容? 是的,我错了...这行不通。我现在正在使用 iResult,但它仍然给我同样的错误。我正在尝试通过 TCP 顺便说一句 【参考方案1】:

有多个问题:

1) 反序列化:

receivedData->ParseFromArray((char*)m_TCPRecvbuf, receivedData->packetsize());

parseFromArray 填充之前调用packetSize,请改用iResult

2) 登录数据:

LoginData data = message.logindata();

这会读取空loginData 的消息副本,您永远不会更改message。使用

LoginData* data = message.mutable_logindata();

获取指向消息 loginData 的指针并在 message 中修改它。

3) 序列化:

int size = message.ByteSize();
void* buffer = malloc(size);

这使用空消息的大小(因为未设置 loginData)。所以你永远不会发送完整的信息。 设置完所有属性后,您必须调用 ByteSize()。这与您的消息中的 packetSize 相矛盾。 由于无论如何您都不能将其用于反序列化(参见 1),因此您应该删除此条目。

【讨论】:

以上是关于协议缓冲区缺少必填字段,但已填写的主要内容,如果未能解决你的问题,请参考以下文章

protobuf-net 缺少可选字段的 has_ 函数?

协议缓冲区:更改字段名称会破坏消息吗?

协议缓冲区:将字段类型从字符串更改为字符串值向后兼容?

谷歌协议缓冲区中的未知扩展

步进器。填写表格,必填字段

检查是不是所有必填字段都填写在特定的 div 中