通过网络接收 Protobuf3 消息

Receiving Protobuf3 Message over Network

我将 Protobuf3 与 c# 一起用于我的客户端,将 c++ 用于我的服务器,其中 .proto 是从相应的 protoc3 编译器生成的。我刚开始使用 Google Protocol Buffers,我目前正在尝试弄清楚如何重新解析我的服务器上从我的客户端发送的接收到的字节,以将其恢复为原始 google::protobuf::Message 我的 C++ 服务器上的对象。

我的客户正在用 c# 发送 CodedOutputStream :

public PacketHandler()
{
  m_client  = GetComponent<ClientObject>();
  m_packet  = new byte[m_client.GetServerConnection().GetMaxPacketSize()];
  m_ostream = new BinaryWriter(new MemoryStream(m_packet));
}

public void DataToSend(IMessage message)
{
  CodedOutputStream output = new CodedOutputStream(m_ostream.BaseStream, true);
  output.WriteMessage(message);
  output.Flush();
  m_client.GetServerConnection().GetClient().Send(m_packet, message.CalculateSize());
}

这似乎有效,现在发送的消息是一个简单的 Ping 消息,如下所示:

// Ping.proto
syntax = "proto3";
package server;

import "BaseMessage.proto";

message Ping {
  base.BaseMessage base = 1;
}

message Pong {
  base.BaseMessage base = 1;
}

我的 BaseMessage 看起来像这样:

// BaseMessage.proto
syntax = "proto3";

package base;

message BaseMessage {
  uint32 size = 1;
}

我的计划是希望从 BaseMessage 扩展我的所有消息,以便最终识别特定消息。一旦我弄清楚了解析和重新解析。

我在 C++ 服务器端收到的消息如下所示:\u0004\n\u0002\b

收到消息时,我试图通过尝试解析接收到的字节来使用 CodedInputStream 对象重新解析。

PacketHandler::PacketHandler(QByteArray& packet, const Manager::ClientPtr client) :
    m_packet(packet),
    m_client(client)
{
  //unsigned char buffer[512] = { 0 };
  unsigned char data[packet.size()] = { 0 };
  memcpy(data, packet.data(), packet.size());

  google::protobuf::uint32 msgSize;
  google::protobuf::io::CodedInputStream inputStream(data, packet.size());
  //inputStream.ReadVarint32(&msgSize);
  //inputStream.ReadRaw(buffer, packet.size());
  server::Ping pingMsg;
  pingMsg.ParseFromCodedStream(&inputStream);
  qDebug() << pingMsg.base().size();
}

这是我有点不确定将消息重新解析为特定消息需要完成的过程的地方。我相信,如果我使用扩展所有消息的 BaseMessage,这将使我能够识别特定的消息,这样我就知道要创建哪一个消息。但是,在我知道这将是 Ping 消息的当前测试中,ParseFromCodedStream 似乎没有创建原始消息。我的推理来自于我的 qDebug() pingMsg.base().size() 不是在我的 c# 客户端发送阶段设置的正确值。

我通过将发送的字节数增加 1 来解决这个问题。

public void DataToSend(IMessage message)
{
  CodedOutputStream output = new CodedOutputStream(m_ostream.BaseStream, true);
  output.WriteMessage(message);
  output.Flush();
  (m_ostream.BaseStream as MemoryStream).SetLength(0); // reset stream for next packet(s)
  m_client.GetServerConnection().GetClient().Send(m_packet, message.CalculateSize() + 1);
}

我仍然有点怀疑为什么我需要添加一个。我认为它一定与空终止符有关。但是,在没有添加之前,我的消息将如下所示:#\u0012!\n\u001Ftype.googleapis.com/server.Pin

然后添加它似乎所做的就是添加正确的消息:#\u0012!\n\u001Ftype.googleapis.com/server.Ping

此外,在此过程中,我想出了如何使用 protobuf3 Any 类型对我的消息进行分类。现在我的 BaseMessage 定义为:

syntax = "proto3";

package base;

import "google/protobuf/any.proto";

message BaseMessage {
  uint32 size = 1;
  google.protobuf.Any msg = 2;
}

这允许我将任何类型的 google::protobuf::Message 放入 msg 字段中,在 ParsingFromCodedStream 上我可以检查它是否是该特定消息。

PacketHandler::PacketHandler(QByteArray& packet, const Manager::ClientPtr client) :
    m_packet(packet),
    m_client(client)
  {
    unsigned char data[packet.size()] = { 0 };
    memcpy(data, packet.data(), packet.size());

    google::protobuf::io::CodedInputStream inputStream(data, packet.size());
    // read the prefixed length of the message & discard
    google::protobuf::uint32 msgSize;
    inputStream.ReadVarint32(&msgSize);
    // -----
    // collect the BaseMessage & execute functionality based on type_url
    base::BaseMessage msg;
    if (!msg.ParseFromCodedStream(&inputStream))
    {
      qDebug() << msg.DebugString().c_str();
      return;
    }

    if (msg.msg().Is<server::Ping>())
    {
      server::Ping pingMsg;
      msg.msg().UnpackTo(&pingMsg);
    }
  }