此时如果客户端也是netty,那么通讯没有问题,但如果是非java语言,那么如果直接发送/接受protobuf序列化后的二进制包,会报以下异常:
io.netty.handler.codec.DecoderException: com.google.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
服务端采用了:
pipeline.addLast(“frameDecoder”, new ProtobufVarint32FrameDecoder());
pipeline.addLast(“frameEncoder”,
new ProtobufVarint32LengthFieldPrepender());
在对每个protobuf消息进行发送前,会加一个消息的长度,同时解码的时候,也需要先解析这个长度再解析消息,以免出现半包、粘包问题。
以python为例
//把protobuf对象传过来
def sendSig(self,sig):
//把protobuf数据反序列化
print("=>", sig.SerializeToString())
value = len(sig.SerializeToString())
bits = value & 0x7f
value >>= 7
while value:
self.send_queue.put(six.int2byte(0x80 | bits))
bits = value & 0x7f
value >>= 7
self.send_queue.put(six.int2byte(bits))
self.send_queue.put(sig.SerializeToString())
详解 self.send_queue.put 这个可以理解是个队列,首先要定义一个队列把数据存入进去! 具体编码细节可以搜索Varint32!在socket发送时候 取得时候会有一个bug!有时候回去三次或两次,在发送时候请判断好
至于接受消息,凑合着用吧
class FFRespReciver(Thread):
PARSING_LEN = 0
PARSING_MSG = 1
def __init__(self,ffconnector):
super(FFRespReciver,self).__init__()
self.ffconnector = ffconnector
self.data_buffer = b""
self.parse_status = FFRespReciver.PARSING_LEN
self.msg_len = 0
def run(self):
while True:
data = self.ffconnector.recv_queue.get()
for b in data:
if self.parse_status == FFRespReciver.PARSING_LEN:
self.data_buffer += six.int2byte(b)
if not (b & 0x80):
self.msg_len = DecodeVarint(self.data_buffer)
self.parse_status = FFRespReciver.PARSING_MSG
self.data_buffer = b""
continue
elif self.parse_status == FFRespReciver.PARSING_MSG:
self.data_buffer += six.int2byte(b)
if len(self.data_buffer) == self.msg_len:
sig = SIG.Sig()
sig.ParseFromString(self.data_buffer)
self.process(sig)
self.data_buffer = b""
self.msg_len = 0
self.parse_status = FFRespReciver.PARSING_LEN
def process(self,sig):
print(sig)
//do the dirty job
def DecodeVarint(buffer):
mask = (1 << 32) - 1
result_type = int
result = 0
shift = 0
for b in buffer:
result |= ((b & 0x7f) << shift)
shift += 7
if shift >= 64:
raise Exception('Too many bytes when decoding varint.')
result &= mask
result = result_type(result)
return result
即用个状态机,先接受消息长度,解析后,接受消息并处理,然后继续接受消息长度。