twisted使用protobuf的网络通信方案

protobuf,google开源的序列化结构数据的方案,官方提供提供了 C++、Java、Python 三种语言的 API,安装使用方法详见:https://github.com/google/protobuf

Python中序列/反序列消息时,有两种实现,pure-Python implementation 和 C++ implementation,当然后者性能更高,详见: https://github.com/google/protobuf/tree/master/python

网络通信中,接收方收到数据后,若要使用protobuf进行反序列化,必须要先知道是什么消息,如何确定收到的数据是什么消息呢?protobuf序列化后的数据未包含数据的长度信息,也无终结符,又要如何区分不同的消息呢?

这两问题,可以通过加固定长度的消息头解决。消息头有两部分,一部分是protobuf序列化后的数据长度(len_pb_data), 另一部分是消息名这个字符串的长度(len_msg_name)。实际发送每条消息的包含 len_pb_data,len_msg_name,msg_name, pb_data这四部分内容,如下图:

具体代码如下,调用send方法发送消息,序列化msg,并在前面加上len_pb_data,len_msg_name, msg_name。

twisted服务器收到消息后,会触发dataReceived处理收到的数据,先解析出pb_data,msg_name的长度,然后解析出msg_name,最后根据msg_name使用protobuf进行反序列化。reactor.callLater(0, _func, self, _request)就是调用相应的消息处理函数。

class PbProtocol(protocol.Protocol, policies.TimeoutMixin):
    BUFFER = ''
    timeOut = 500
    header_format = 'IH'
    header_length = struct.calcsize(header_format)
    def connectionMade(self):
        self.transport.setTcpKeepAlive(True)
        self.setTimeout(self.timeOut)
        peer = self.transport.getPeer()

        print 'Connection made. host, port:', peer.host, peer.port

    def dataReceived(self, data):
        self.resetTimeout()
        self.transport.pauseProducing()
        self.BUFFER += data
        buffer_length = len(self.BUFFER)
        _l = ''
        while (buffer_length >= self.header_length):
            len_pb_data, len_msg_name = struct.unpack(self.header_format, self.BUFFER[:self.header_length])#_bound.ParseFromString(self.BUFFER[:8])
            if len_msg_name:
                if len_msg_name > len(self.BUFFER[self.header_length:]):
                    print 'not enough buffer for msg name, wait for new data coming ...   '
                    break
                else:
                    msg_name = struct.unpack('%ds'% len_msg_name,  self.BUFFER[self.header_length:len_msg_name + self.header_length])[0]
                    _func = getattr(self.factory.service, '%s' % msg_name.lower(), None) 
                    _msg =  getattr(login_pb2, msg_name, None)
                    if _func and _msg:
                        _request = getattr(login_pb2, msg_name)()
                        if len_pb_data <= len(self.BUFFER[self.header_length + len_msg_name :]):
                            _request.ParseFromString(self.BUFFER[self.header_length + len_msg_name : self.header_length + len_msg_name + len_pb_data])
                            reactor.callLater(0, _func, self, _request) 
                            self.BUFFER = self.BUFFER[self.header_length + len_msg_name + len_pb_data:]
                            buffer_length = len(self.BUFFER) 
                            continue
                        else:   
                            print 'not enough buffer for pb_data, waiting for new data coming ... '
                            break
                    else:
                        print 'no such message handler. detail:', _func, hasattr(login_pb2, msg_name), repr(self.BUFFER)
                        if self.fromclient:
                            self.transport.loseConnection()
                        else:
                            self.BUFFER = ''

                        return
            else:
                print 'Un-supported message, no msg_name. detail:', len_msg_name
                if self.fromclient:
                    self.transport.loseConnection()
                else:
                    self.BUFFER = ''
                return
            
        self.transport.resumeProducing()
        

    def send(self, msg):
        if msg:
            pb_data = msg.SerializeToString()
            _header = struct.pack(self.header_format + '%ds'%len(msg.__class__.__name__), len(pb_data), len(msg.__class__.__name__), msg.__class__.__name__)
            self.transport.write(_header + pb_data)

以上就是twisted中protobuf的实现,完整源码详见:https://github.com/mxins/twisted-protobuf

loginserver是twisted实现的登陆服务器,robot是登陆机器人,base.py实现了PbProtocol类,login.proto是消息格式定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值