您描述的内容实际上已经得到了C++和Java中的协议缓冲区实现的支持。您所要做的就是传输一个(如google/protobuf/descriptor.proto
中所定义)包含表示每个相关文件的s,然后用于解释接收端的消息。FileDescriptorSet
FileDescriptorProto
.proto
DynamicMessage
要获取 in C++,给定该文件中定义的消息类型,请执行以下操作:FileDescriptorProto
Foo
google::protobuf::FileDescriptorProto file;
Foo::descriptor().file()->CopyTo(&file);
将定义所需类型的所有 s 以及它们导入的所有文件放入原型中。请注意,您可以使用 google::p rotobuf::FileDescriptor
(返回的内容)来迭代依赖项,而不是显式命名每个依赖项。FileDescriptorProto
FileDescriptorSet
Foo::descriptor().file()
现在,将 发送到客户端。FileDescriptorSet
在客户端上,使用 FileDescriptor.buildFrom()
将每个都转换为实时描述符.FileDescriptor
。您必须确保在依赖项之前构建依赖项,因为在构建依赖项时,您必须提供已构建的依赖项来构建From()。
FileDescriptorProto
从那里,您可以使用 FileDescriptor
的 findMessageTypeByName()
来查找您关注的特定消息类型的描述符
。
最后,您可以调用 DynamicMessage.newBuilder(描述符)
来为相关类型构造一个新的生成器实例。DynamicMessage.Builder
实现了 Message.Builder
接口,该接口具有 getField()
和 setField()
等字段,用于动态操作消息的字段(通过指定相应的 FieldDescriptor
s)。
同样,您可以调用 DynamicMessage.parseFrom(描述符,输入)
来解析从服务器接收的消息。
请注意,动态消息的
一个缺点是它相对较慢。从本质上讲,它就像一种解释型语言。生成的代码速度更快,因为编译器可以针对特定类型进行优化,而 DynamicMessage
必须能够处理任何类型。
但是,这真的没有办法解决这个问题。即使您在运行时运行代码生成器并编译了该类,在您知道要使用的类型之前,实际使用新类的代码仍然是您之前编写的代码。因此,它仍然必须使用反射或类似反射的接口来访问消息,这将比为特定类型手写代码要慢。
但这是一个好主意吗?
好吧,这取决于。客户端实际上将如何处理从服务器接收的此架构?通过网络传输模式并不能神奇地使客户端与该协议的版本兼容 - 客户端仍然必须了解协议的含义。如果协议以向后不兼容的方式进行了更改,这几乎肯定意味着协议的含义已更改,并且必须更新客户端代码,无论是否进行架构传输。唯一可以期望客户端在没有更新的情况下继续工作的情况是客户端仅执行仅依赖于消息内容但不依赖于消息含义的通用操作 - 例如,客户端可以将消息转换为 JSON,而不必知道它的含义。但这是相对不寻常的,特别是在应用程序的客户端。这正是Protobufs默认情况下不发送任何类型信息的原因 - 因为它通常是无用的,因为如果接收者不知道含义,架构就无关紧要。
如果问题是服务器向客户端发送的消息根本不打算解释,而只是在以后发送回服务器,那么客户端根本不需要架构。只需将消息传递为,不要费心解析它。请注意,包含类型编码消息的字段在网络上看起来与类型实际声明为 的字段完全相同。您实际上可以针对略有不同的文件版本编译客户端和服务器,其中客户端看到的是特定字段,而服务器将其视为子消息,以避免客户端需要了解该子消息的定义。``bytes
bytes
Foo
Foo
.proto
bytes