Differences between revisions 1 and 2
Revision 1 as of 2004-08-09 01:46:12
Size: 4004
Editor: dreamingk
Comment:
Revision 2 as of 2004-11-28 09:02:14
Size: 5811
Editor: 203
Comment: 一个读者的冒昧发言(看完请您删掉):
Deletions are marked like this. Additions are marked like this.
Line 70: Line 70:
这时我又发现,这段代码还可以成功运行。不但一个客户,多个client时也能正常运行下去。 这时我又发现,这段代码还可以成功运行。不但一个客户
Line 72: Line 72:
这时又有了新的疑问:
 * dataReceived的data参数难道说知道我有没读完?没读完下回还会将没读完的东东再给我送过来?
 * 还有就是,多个客户访问的情况下,data不会把几个客户机的数据混起来吗?
Line 76: Line 73:
感觉这块代码很值得分析一下,哪位大侠出手给大家解析一下这里的运行机制?
Line 78: Line 74:
''----HD
我工作我快乐,我勤奋我收获。请与我一起快乐,与我一起收获。
''
为了更清晰一些,把问题单独列出来:
 * 1.什么样的原理就会让_ _buffer能在try 10次的过程中加足了四个字节呢?
 * 2.dataReceived的data参数难道说知道我有没读完?没读完下回还会将没读完的东东再给我送过来?
 * 3.多个客户访问的情况下,data不会把几个客户机的数据混起来吗?
-----------------------------------------------------------------
{{{
一个读者的冒昧发言(看完请您删掉):
}}}
Line 86: Line 79:
== 思考回答 == 恕我冒昧,这篇文章虽未写完,但我已经不忍心看您写下去了。
Line 88: Line 81:
我想您对于“流”这个概念理解还不够,恰好我刚完成一个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 to 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) # line A
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 B
}}}

上面代码中,最后面几行是测试代码,line A处发送了一个不完整的包,line B处不断补全了这个包,还把另2个包一起发过去了,网络数据流经常会有这种情况,所以必须要处理这种情况。经测试,上面代码很好地完成了功能。

不过还需要注意的是,上面的数据包处理代码没有经过校验,可能存在重大问题。比如包格式错误(网络上传输的数据谁能保证没有出错呢),如果解出一个很大的长度值,则程序将分不出其它包的间隔了,这些是我上面输出'Error! Error! Error!'的地方要处理的。

当然上面提到的这个问题不在本文讨论之列,不再详述了。

含有章节索引的中文 文章模板 -- dreamingk [DateTime(2004-08-09T01:46:12Z)] TableOfContents

题面

{{{HD hdcola at gmail.com Mon Jul 26 09:30:37 HKT 2004 }}} 这两天在做流的识别与传输,遇到一个小小问题,与大家一起讨论。 :)

另外我将邮件的名命名为[日期][本周代码分析],希望以后我们每周讨论一段有价值的代码,以加深大家对python、twisted、网络开发、开发技巧方面的了解。 :)

我先提出本周的代码分析,是我在twisted中使用时遇到的问题。希望我们这周能分析出它的机制来。 :) 了解清它内部的机理会有利于我们将来的应用。

我在自己的protocol中有如下代码:

   1     def dataReceived(self, data):
   2         """ 接收数据报头,判断报文大小,接收报文数据"""
   3         self.__buffer = self.__buffer + data
   4         (length,) = struct.unpack('>I', self.__buffer[:4])

在系统运行时间长一点时会发现python报出一个Exception,说是format不一至。

这时仔细考虑了问题的原因是在于对方发送tcp流的速度不快,
导致data中(也就是__buffer中)的数据没有4个字节,这时使用struct.unpack会出现解不出一个I的情况来。

于是,我对代码做了如下改动:

   1     def dataReceived(self, data):
   2         """ 接收数据报头,判断报文大小,接收报文数据"""
   3         self.__buffer = self.__buffer + data
   4 
   5         while len(self.__buffer):
   6             trynum = 0
   7             while True:
   8                 try:
   9                     (length,) = struct.unpack('>I', self.__buffer[:4])
  10                     break
  11                 except Exception :
  12                     print "unpack error"
  13                     trynum += 1
  14                     if (trynum > 10):
  15                         break

呵呵,办法显的不是哪么的巧妙,但是通常10次之内真的能将正确的报头(头四个字节)收下来,以让程序正确的运行下去。但是这个东东让我感觉很是不爽,为什么呢?因为占用太多的cpu,即使加入了sleep,也违反了异步工作的机理。但是这个方法已经让我出现了疑惑,它怎么可能就成功了呢?

self.__buffer = self.__buffer + data
怎么就会让__buffer能在try 10次的过程中加足了四个字节呢?

我非常有兴趣的将代码改了一下,因为我想看看twisted是不是真的会很"神奇":

   1     def dataReceived(self, data):
   2         """ 接收数据报头,判断报文大小,接收报文数据"""
   3         self.__buffer = self.__buffer + data
   4 
   5         while len(self.__buffer):
   6                 try:
   7                     (length,) = struct.unpack('>I', self.__buffer[:4])
   8                     break
   9                 except Exception :
  10                     print "unpack error"
  11                     return

这时我又发现,这段代码还可以成功运行。不但一个客户壘


一个读者的冒昧发言(看完请您删掉):

恕我冒昧,这篇文章虽未写完,但我已经不忍心看您写下去了。

我想您对于“流”这个概念理解还不够,恰好我刚完成一个python的服务器项目,用的就是twisted,虽然理解还不够深刻,但多多少少还是有些了解的。

您在dataReceived函数中的处理不合常理,要知道网络数据流没人保证它一次传递几个字节过来,然后调用一次你的处理函数。所以有可能一个数据包的接收要多次调用你的处理函数,也可能只调用一次,也可能一次调用里面有多个数据包,所以这时处理要仔细了。

我根据您的代码稍做了一下修改:

   1 import struct
   2 
   3 class Test:
   4 
   5     def __init__ (self):
   6         self.__buffer = ''
   7 
   8     def dataReceived(self, data):
   9         self.__buffer = self.__buffer + data
  10 
  11         while len(self.__buffer) >= 4:
  12             (length,) = struct.unpack('I', self.__buffer[:4])
  13             body = self.__buffer[4:4+length]
  14             if len (body) == length:
  15                 self.onMessage (body)
  16                 self.__buffer = self.__buffer[4+length:]
  17             elif len(body) < length:
  18                 print 'Pack length not enough, wait to next receive'
  19                 break
  20             else:
  21                 print 'Error! Error! Error!'
  22                 assert False
  23         print 'Buffer length:', len (self.__buffer)
  24 
  25     def onMessage (self, msg):
  26         print 'onMessage:', msg
  27 
  28 t = Test ()
  29 str = 'People\'s Republic of China'
  30 data = struct.pack ('I', len(str))
  31 data += str[:3]
  32 t.dataReceived (data)    # line A
  33 data = str[3:]
  34 str = 'Python world'
  35 data += struct.pack ('I', len(str))
  36 data += str
  37 str = 'Twisted network framework'
  38 data += struct.pack ('I', len(str))
  39 data += str
  40 t.dataReceived (data)    # line B

上面代码中,最后面几行是测试代码,line A处发送了一个不完整的包,line B处不断补全了这个包,还把另2个包一起发过去了,网络数据流经常会有这种情况,所以必须要处理这种情况。经测试,上面代码很好地完成了功能。

不过还需要注意的是,上面的数据包处理代码没有经过校验,可能存在重大问题。比如包格式错误(网络上传输的数据谁能保证没有出错呢),如果解出一个很大的长度值,则程序将分不出其它包的间隔了,这些是我上面输出'Error! Error! Error!'的地方要处理的。

当然上面提到的这个问题不在本文讨论之列,不再详述了。

PyTwisted/codeAnalyze-040726-twisted的数据接收机制分析 (last edited 2009-12-25 07:09:20 by localhost)