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