protobufjs中protobuf.encode()功能不符合预期问题求助
问题详情
使用protobufjs v7.2.3版本时,调用ReceiverSetting.encode()后生成的buffer仅为<Buffer 12 00>,解码后大部分字段丢失,仅保留domainOverride和listen字段。protobuf.verify()校验无错误,属性已按代码设置,文件及导入流程正常,其他场景同类使用无异常,该问题已耗时4天未解决。
代码片段
let settings = { port_list: { range: [{ From: 1, To: 100 }] }, listen: {}, allocation_strategy: {}, stream_settings: {}, receive_original_destination: {}, sniffing_settings: { enabled: false } } let error = ReceiverSetting.verify(settings) let receiverSetting = ReceiverSetting.create(settings) console.log('error: ', error) const buffer = ReceiverSetting.encode(receiverSetting).finish(); console.log('receiverSetting:', receiverSetting); console.log('buffer:', buffer); const decodedMessage = ReceiverSetting.decode(buffer); console.log('decodedMessage:', decodedMessage);
日志输出
error: null receiverSetting: ReceiverConfig { domainOverride: [], port_list: { range: [ [Object] ] }, listen: {}, allocation_strategy: {}, stream_settings: {}, receive_original_destination: {}, sniffing_settings: {} } buffer: <Buffer 12 00> decodedMessage: ReceiverConfig { domainOverride: [], listen: IPOrDomain {} }
对应Proto文件定义
syntax = "proto3"; package xray.app.proxyman; import "common/net/address.proto"; import "common/net/port.proto"; import "transport/internet/config.proto"; import "common/serial/typed_message.proto"; // ... 省略其他定义 ... message ReceiverConfig { xray.common.net.PortList port_list = 1; xray.common.net.IPOrDomain listen = 2; AllocationStrategy allocation_strategy = 3; xray.transport.internet.StreamConfig stream_settings = 4; bool receive_original_destination = 5; reserved 6; repeated KnownProtocols domain_override = 7 [ deprecated = true ]; SniffingConfig sniffing_settings = 8; } message SniffingConfig { // Whether or not to enable content sniffing on an inbound connection. bool enabled = 1; // Override target destination if sniff'ed protocol is in the given list. // Supported values are "http", "tls", "fakedns". repeated string destination_override = 2; repeated string domains_excluded = 3; // Whether should only try to sniff metadata without waiting for client input. // Can be used to support SMTP like protocol where server send the first // message. bool metadata_only = 4; bool route_only = 5; } // ... 省略其他定义 ...
问题分析与解决办法
1. 字段名大小写不匹配
代码中port_list.range使用大写开头的From和To,但Proto3规范中字段名默认采用蛇形命名(如from、to),protobufjs会严格匹配Proto文件中的字段名。若xray.common.net.PortRange定义为:
message PortRange { int32 from = 1; int32 to = 2; }
则代码需改为小写字段名,否则verify不会报错,但create时会忽略不匹配字段,导致port_list无有效内容被编码:
port_list: { range: [{ from: 1, to: 100 }] }
2. 布尔类型字段赋值错误
receive_original_destination是Proto定义的bool类型,代码中赋值为空对象{}类型不匹配,编码时会直接忽略该字段,需改为布尔值:
receive_original_destination: false // 或true,根据业务需求设置
3. 嵌套消息类型初始化问题
allocation_strategy、stream_settings是自定义消息类型,直接传空对象{}无法被protobufjs识别为合法实例,编码时会跳过。需使用对应类型的create方法初始化:
allocation_strategy: AllocationStrategy.create({}), stream_settings: StreamConfig.create({})
(前提是这些类型已正确导入并加载)
4. 默认值字段未编码
Proto3中布尔类型默认值为false,sniffing_settings.enabled = false属于默认值,protobufjs默认不会编码默认值字段。若需强制编码,需在encode时添加alwaysEncodeDefaults选项:
const buffer = ReceiverSetting.encode(receiverSetting, { alwaysEncodeDefaults: true }).finish();
5. 嵌套类型未正确加载
确保PortList、IPOrDomain、AllocationStrategy、StreamConfig、SniffingConfig等所有依赖的Proto类型都已通过protobufjs正确加载(比如使用load或loadSync方法加载所有相关Proto文件),否则create方法无法解析嵌套对象,导致字段被忽略。
内容的提问来源于stack exchange,提问作者Ezioadf2

