深圳夜归人

繁华的都市,有谁记得我们的脚步?

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

通常我们在编写网络应用时,会制定自己的协议格式。

我们的协议一般是在TCP协议层上建立的,TCP是一个流式协议,它每次接收的不一定和我们send的字节数相同,所以我们的协议通常会有2层:第一层分割数据包(包协议),第二层才是把数据包解析成我们自己的协议格式,通常这2层非常靠近,以致于我们不愿意去区分它。

本文打算完成一个简单的协议处理框架,采用的协议是一个简单的基于字符串的协议。在上一篇文章里,我们完成了一个简单的客户端,本章就继续在这个客户端的基础上完成,当然存在一个问题是没有服务器去测试。我想其实做些简单的测试并不需要真的存在一个服务器,何况我们这个协议是简单的字符串协议,模拟字符串收到的过程就可以完成测试了。

就网络应用来说,除了连接过程有区别以外,连接以后我们不用去区分哪台是客户机,哪台是服务器,我们只要规定它们用一样的协议格式来通信。我编写了这个简单的协议处理框架,并且预留了一个协议扩充的功能,代码如下:

from twisted.internet.protocol import Protocol
from sys import stdout
import logging, struct

# 我习惯这样使用logger
LOG = logging.getLogger ()

# Message类,只要实现了getAction的类,都可以与这个框架一起使用
class Message:
    def __init__(self, body
=''):
        self.body = body
    def getAction(self):
        return 
0

# Message工厂类,这个类与Message一起完成协议解析
class MessageFactory:
    def parseMessage(self, data):
        
if len(data) < 4:
            return None, data

        (length,) 
= struct.unpack('I', data[:4])
        body = data[4:4+length]
        
if len (body) == length:
            data 
= data[4+length:]
            msg 
= Message(body)
            return msg, data
        elif 
len(body) > length:
            print 
'Error! Error! Error!'
            assert False
        return None, data
    
    def packMessage(self, msg):
        return struct.pack (
'I', len(msg.body)) + msg.body


以上2个类是用户协议处理部分,这个协议比较简单,4字节的长度值加上与此长度匹配的字符串构成一个包。

下面的ConnectionHandle类是协议处理框架,它的__init__方法接受一个message factory,每次收到数据,将调用factory的parseMessage处理,每次发送message,也使用factory的packMessage把message串化。我去掉了前一章例子中的一些代码,这样看起来会更清晰。

当完整解析出一个message时,会查找通过addHandler函数注册的message处理器。本例因为都是文本字符串,所以Message.getAction()返回的值也相同,各个message也会调用同一个处理器。

class ConnectionHandle (Protocol):

    def __init__ (self, msgFactory, 
**args):
        self._msgFactory 
= msgFactory
        self._recvBuf 
= ''
        self._handlers = { }
        
    def onMessage0(self, msg):
        print 
'received message:', msg.body
        
    def addHandler (self, action, handler):
        self._handlers.setdefault (action, []).append (handler)
        
    def sendMessage (self, msg):
        self.transport.write (self._msgFactory.packMessage (msg))
        
    def dataReceived (self, data):
        self._recvBuf 
+= data

        
while True:
            msg, self._recvBuf 
= self._msgFactory.parseMessage (self._recvBuf)
            
if msg is None:
                break
            self.onMessage (msg)
        
    def onMessage (self, msg):
        action 
= msg.getAction ()
        
if action in self._handlers:
            
LOG.debug('Received message: %s', str(action))
            for handler in self._handlers[action]:
                handler (msg)
        
else:
            self.onUnprocessedMessage (msg)
            
    def onUnprocessedMessage (self, msg):
        
LOG.warning ('Unprocessed message %s', str(msg.getAction ()))


以下是测试代码,OnMsg是一个message处理器,因为这部分测试不需要reactor,所以我模拟了数据“流”入dataReceived函数的过程。

# message处理器

def OnMsg(msg):
    print 
'received message:', msg.body

msgFactory 
= MessageFactory()
# 把MessageFactory传进去,如果有不同的协议解析器,则可灵活使用
connection 
= ConnectionHandle(MessageFactory()) 
connection.addHandler(
0, OnMsg)

# 初始化模拟数据,把2个message串化

msg 
= Message()
msg.body 
= 'China'
buffer = msgFactory.packMessage(msg)
msg.body 
= 'People\'s Republic of China'
buffer += msgFactory.packMessage(msg)

# 模拟数据“流”入dataReceived的过程,下面这些pos用来模拟每次发送后的位置

bytes_pos 
= [037910111415172128323539]
bytes_pos 
= [i for i in bytes_pos if i < len(buffer)]
bytes_pos.append(
len(buffer))

for i in range(len(bytes_pos) - 1):
    start, 
end = bytes_pos[i], bytes_pos[i+1]
    connection.dataReceived(buffer[start:
end])

之所以把它称为框架,是因为Message和MessageFactory是作为参数传递进去的,要实现新的协议,只需要重新编写这2个类就可以了。

另外,message处理器可以是类成员方法,不像C语言里面那样,要使用回调函数,这得益于python语言的灵活。

posted on 2004-12-28 15:59  cpunion  阅读(731)  评论(0编辑  收藏  举报