基于python的protobuf通信(添加消息头)

最近基于Python做一个客户端,本身自己对Python不太熟悉,而且服务器是使用protobuf协议进行通信,之前主要是使用xml协议做通信,对protobuf也不了解,当然protobuf与xml类似,都有树形结构,而且貌似效率高些。对protobuf的Python使用有很多教程,其实主要来源于对官方文档的翻译,下面放出链接:

https://developers.google.com/protocol-buffers/docs/pythontutorial?hl=zh-CN

protobuf在使用中需要将定义的.proto文件转换为.py文件,使用中将文件导入做消息的填充和读取。常规的方法需要安装protobuf,其实只要做文件转换的话可以更简单的操作。在官方的release版本下面找到protoc-3.X.X-winXX.zip,然后解压后找到protoc.exe,这是一个在windows下可执行文件,可以直接完成转换操作。

然后就可以在cmd界面使用protoc命令进行转换,命令格式为:

protoc -I=源地址路径 --python_out=目标地址路径  源地址路径/xxx.proto

python中protobuf文件的使用教程很多,主要步骤是将文件导入,利用文件对象初始化消息,然后进行消息填充。

 message = Message_pb2.Message() #新建信息
   

对于枚举类型,可以直接调用使用,例如:

define:
enum MSG_TYPE {
  Message_Error = 0;
  Login_Request = 1;
  Login_Response = 2;
  Heartbeat_Request = 3;
  Heartbeat_Response = 4;
  ConnectivityCheck_Request = 5;
  ConnectivityCheck_Response = 6;
  
}

python:
 message.msg_type = Message_pb2.Heartbeat_Request

对于多层消息的填充,python使用比较简单,基本直接调用,如:

login = message.request.login
login.username = "jiang"
login.password = "123456"
login.host_name = hostname

repeated类型的字段貌似要用add方法添加,没有使用过-_-  。

还有一类是map类型的,我是这样填充的:

define:
message HeartbeatRequest
{
  map<string, ApInfo> ap_maps = 1;//key是ssid,每个ssid取一个最强的(暂时考虑)
  string ssid = 2;//当前正在监控的ssid
}
message ApInfo{
	
  string ssid = 1;
  string bssid = 2;
  string signals = 3;
  string channel = 4;
 
}

python:
 heartbeat = message.request.heartbeat
 heartbeat.ap_maps[k].ssid=v.ssid
 heartbeat.ap_maps[k].bssid =v.bssid
 heartbeat.ap_maps[k].signals = str(v.signal)
 heartbeat.ap_maps[k].channel = str(self.getChannel(v.freq))

另外还有一个问题就是protobuf通信时通信双方并不知道消息的大小,假如不加处理的话,通信双方会出现粘包或者包断掉的问题,一般处理是加上消息头,消息定义一般是由len_pb_data和len_msg_name、msg_name、pb_data,len_pb_data和len_msg_name分别表示有效负载大小(消息序列化后长度)和消息名长度,msg_name表示消息名来唯一标识消息,pb_data就是序列化后的消息。消息头的处理当然可以自己定义,但是为了与其他端进行通信最好还是采用官方提供的方法,但是python官方文档对此的说明不多,最后还是阅读源码找到了google.protobuf.internal里面的encoder和decoder来处理消息头,具体处理如下:

    from google.protobuf.internal import encoder, decoder
    #封装message
    def send(self, msg):
        '''pack request to delimited data'''
        data = msg.SerializeToString()
        data = encoder._VarintBytes(len(data)) + data
        self.s.send(data)

    #解出message数据
    def unpack(self):
        buffer=[]
        '''unpack from delimited data'''
        data=self.s.recv(1024)
        size, position = decoder._DecodeVarint(data, 0)
        if((position + size)>1024):
            buffer.append(data)
            d=self.s.recv(position + size-1024)
            if d:
                buffer.append(d)
            fulldata = b''.join(buffer)
            message = Message_pb2.Message()
            message.ParseFromString(fulldata)
            return message
        else:
            message = Message_pb2.Message()
            message.ParseFromString(data[position:position + size])
            return message

这里主要调用了encoder里面的_VarintBytes方法和decoder里面的_DecodeVarint方法,我这里为了防止消息过长被截断,还做了多次读取的准备,有更好的方法欢迎交流,至于消息头具体怎么加上的方法可以自己去阅读源码,我这里就不分析了。

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值