0

我将 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# 客户端的发送阶段设置的正确值。

4

1 回答 1

0

我能够通过将发送的字节数增加 + 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);
    }
  }
于 2016-07-24T00:58:55.600 回答