##language:zh ''' 含有章节索引的中文 文章模板 ''' -- dreamingk [<>] <> = 题面 = {{{HD hdcola at gmail.com Mon Jul 26 09:30:37 HKT 2004 }}} 这两天在做流的识别与传输,遇到一个小小问题,与大家一起讨论。 :) 另外我将邮件的名命名为[日期][本周代码分析],希望以后我们每周讨论一段有价值的代码,以加深大家对python、twisted、网络开发、开发技巧方面的了解。 :) 我先提出本周的代码分析,是我在twisted中使用时遇到的问题。希望我们这周能分析出它的机制来。 :) 了解清它内部的机理会有利于我们将来的应用。 我在自己的protocol中有如下代码: {{{ #!python def dataReceived(self, data): """ 接收数据报头,判断报文大小,接收报文数据""" self.__buffer = self.__buffer + data (length,) = struct.unpack('>I', self.__buffer[:4]) }}} 在系统运行时间长一点时会发现python报出一个Exception,说是format不一至。 {{{ 这时仔细考虑了问题的原因是在于对方发送tcp流的速度不快, 导致data中(也就是__buffer中)的数据没有4个字节,这时使用struct.unpack会出现解不出一个I的情况来。 }}} 于是,我对代码做了如下改动: {{{ #!python def dataReceived(self, data): """ 接收数据报头,判断报文大小,接收报文数据""" self.__buffer = self.__buffer + data while len(self.__buffer): trynum = 0 while True: try: (length,) = struct.unpack('>I', self.__buffer[:4]) break except Exception : print "unpack error" trynum += 1 if (trynum > 10): break }}} 呵呵,办法显的不是哪么的巧妙,但是通常10次之内真的能将正确的报头(头四个字节)收下来,以让程序正确的运行下去。但是这个东东让我感觉很是不爽,为什么呢?因为占用太多的cpu,即使加入了sleep,也违反了异步工作的机理。但是这个方法已经让我出现了疑惑,它怎么可能就成功了呢? {{{ self.__buffer = self.__buffer + data 怎么就会让__buffer能在try 10次的过程中加足了四个字节呢? }}} 我非常有兴趣的将代码改了一下,因为我想看看twisted是不是真的会很"神奇": {{{ #!python def dataReceived(self, data): """ 接收数据报头,判断报文大小,接收报文数据""" self.__buffer = self.__buffer + data while len(self.__buffer): try: (length,) = struct.unpack('>I', self.__buffer[:4]) break except Exception : print "unpack error" return }}} 这时我又发现,这段代码还可以成功运行。不但一个客户壘 == “流”这个概念 == ----------------------------------------------------------------- {{{ 一个读者的冒昧发言(看完请您删掉): }}} 恕我冒昧,这篇文章虽未写完,但我已经不忍心看您写下去了。 我想您对于“流”这个概念理解还不够,恰好我刚完成一个python的服务器项目,用的就是twisted,虽然理解还不够深刻,但多多少少还是有些了解的。 您在dataReceived函数中的处理不合常理,要知道网络数据流没人保证它一次传递几个字节过来,然后调用一次你的处理函数。所以有可能一个数据包的接收要多次调用你的处理函数,也可能只调用一次,也可能一次调用里面有多个数据包,所以这时处理要仔细了。 我根据您的代码稍做了一下修改: {{{ #!python import struct class Test: def __init__ (self): self.__buffer = '' def dataReceived(self, data): self.__buffer = self.__buffer + data while len(self.__buffer) >= 4: (length,) = struct.unpack('I', self.__buffer[:4]) body = self.__buffer[4:4+length] if len (body) == length: self.onMessage (body) self.__buffer = self.__buffer[4+length:] elif len(body) < length: print 'Pack length not enough, wait for next receive' break else: print 'Error! Error! Error!' assert False print 'Buffer length:', len (self.__buffer) def onMessage (self, msg): print 'onMessage:', msg t = Test () str = 'People\'s Republic of China' data = struct.pack ('I', len(str)) data += str[:3] t.dataReceived (data[:2]) # line A t.dataReceived (data[2:]) # line B data = str[3:] str = 'Python world' data += struct.pack ('I', len(str)) data += str str = 'Twisted network framework' data += struct.pack ('I', len(str)) data += str t.dataReceived (data) # line C }}} 上面代码中,最后面几行是测试代码,line A处发送了一个不完整的包头,line B处补全了包头但这个包还是没发完整,line C处不但补全了这个包,还把另2个包一起发过去了,网络数据流经常会有这种情况。经测试,上面代码很好地完成了功能。 不过还需要注意的是,上面的数据包处理代码没有经过校验,可能存在重大问题。比如包格式错误(网络上传输的数据谁能保证没有出错呢),如果解出一个很大的长度值,则程序将分不出其它包的间隔了,这些都要单独处理,最好在每个包后面再附加一个分隔符,包分解时判断一下是不是从分隔符处分开的(长度前缀也应保留,可提高效率,2个相互补充)。不要因为一个不重要的坏包影响了整个系统的运行。 当然上面提到的这个问题不在本文讨论之列,不再详述了。 = 反馈 = * 最后说声对不起。我对WIKI这个东东还不会用,不知道怎么跟个帖子,所以直接编辑了您的帖子,但没有修改您所发的内容,望见谅。 * 哈哈哈!谢谢!Wiki 的本质就是公共写作!谁都可以编辑他人的文章!只要是正确的思想!不过应该留下网名,或是邮箱,以便大家进一步交流 -- ZoomQuiet = 联系方式 = * 我就是上面那个冒昧的读者,很有兴趣和大家一起探讨python,我最近才开始用心学python的,不知道大家经常在哪里讨论呢? -- LiJie * 你从首页就应该发现我们的讨论中心 * http://groups-beta.google.com/group/python-cn