通过网络接收Protobuf3消息

时间:2016-07-23 06:32:52

标签: c# c++ protocol-buffers proto3

我正在为我的客户使用带有c#的Protobuf3。我的服务器的c ++,其中.proto是从相应的protoc3编译器生成的。我刚开始使用谷歌协议缓冲区,我正在试图弄清楚如何重新解析我的服务器上从客户端发送的接收字节,将其转回原来的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扩展我的所有消息,以便最终识别特定消息。一旦我得到解析&amp;想通过重新解析。

我在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 个答案:

答案 0 :(得分: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);
}

我仍然有点怀疑为什么我需要添加一个。我认为它必须与null终止符有关。但是,如果没有添加此内容,我的消息将显示为:#\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);
    }
  }