使用协议缓冲区将二进制文件从 Java 服务器发送到 C# Unity3d 客户端

Send binary file from Java Server to C# Unity3d Client with Protocol Buffer

我问过这个问题 但我发现通过内置操作在 Java 和 C# 之间发送文件并不是最佳解决方案,因为我还需要其他消息,不仅是文件内容。

因此,我尝试使用 Protobuf,因为它速度快并且 serialize/deserialize 对象平台无关。我的 .proto 文件如下:

message File{
  optional int32 fileSize = 1;
  optional string fileName = 2;
  optional bytes fileContent = 3;
}

因此,我在生成的 .java 文件中设置了每个变量的值:

file.setFileSize(fileSize);
file.setFileName(fileName);
file.setFileContent(ByteString.copyFrom(fileContent, 0, fileContent.length);

我看了很多关于如何将对象写入文件并从中读取的教程。但是,我找不到任何关于如何将文件从服务器套接字发送到客户端套接字的示例。

我的目的是在java服务器上序列化对象(文件大小、文件名和文件内容)并将这些信息发送到C#客户端。所以,这个文件可以被反序列化并存储在客户端。

在我上面的示例代码中,服务器读取文件(图像文件)的字节并将其写入输出流,以便客户端可以通过输入流将字节读写到磁盘。我想通过序列化生成的 .proto 文件来实现同样的目的。

任何人都可以给我提供一个例子或给我一个如何做的提示吗?

documentation 中所述,protobuf 不会处理消息的开始和停止位置,因此当使用像 TCP 这样的流套接字时,您必须自己做。

来自文档:

[...] If you want to write multiple messages to a single file or stream, it is up to you to keep track of where one message ends and the next begins. The Protocol Buffer wire format is not self-delimiting, so protocol buffer parsers cannot determine where a message ends on their own. The easiest way to solve this problem is to write the size of each message before you write the message itself. When you read the messages back in, you read the size, then read the bytes into a separate buffer, then parse from that buffer. [...]

长度前缀是一个很好的选择。根据您正在编写的语言,有些库可以为例如可以使用的TCP,也可以自己定义。

网络上缓冲区的示例表示形式可能是(左侧缓冲区的开头):

[buf_length|serialized_buffer2]

所以你编码在发送之前打包缓冲区可能看起来像(这是在 javascript 和 node.js 中):

function pack(message) {
  var packet = new Buffer(message.length + 2);

  packet.writeIntBE(message.length, 0, 2);
  message.copy(packet, 2);

  return packet;
}

要阅读你必须做相反的事情:

client.on('data', function (data) {
  dataBuffer = Buffer.concat([dataBuffer, data]);

  var dataLen = dataBuffer.readIntBE(0, 2);

  while(dataBuffer.length >= dataLen) {
    // Message length excluding length prefix of 2 bytes
    var msgLen = dataBuffer.readIntBE(0, 2);

    var thisMsg = new Buffer(dataBuffer.slice(2, msgLen + 2));

    //do something with the msg here

    // Remove processed message from buffer
    dataBuffer = dataBuffer.slice(msgLen + 2);
  }
});

您还应注意,在 TCP 套接字上发送多个 protobuf 时,它们可能会被缓冲以进行网络优化(串联)并一起发送。这意味着无论如何都需要某种分隔符。