Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4971518
  • 博文数量: 921
  • 博客积分: 16037
  • 博客等级: 上将
  • 技术积分: 8469
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-05 02:08
文章分类

全部博文(921)

文章存档

2020年(1)

2019年(3)

2018年(3)

2017年(6)

2016年(47)

2015年(72)

2014年(25)

2013年(72)

2012年(125)

2011年(182)

2010年(42)

2009年(14)

2008年(85)

2007年(89)

2006年(155)

分类: Python/Ruby

2012-03-12 11:30:26

原创文章,转载请注明出处
    前面的工作都已准备就绪,现在我们得来看看服务端怎么和客户端之间进行通信了,Python和FLASH之间的通信,我整理为以下3种:

    1、用现成的协议及类库处理,比如:pyamf
    2、自己封包进行二进制数据流通信
    3、用JSON字符串通信

一、JSON和二进制数据流的优缺比较
    pyamf有比较现成的文档,因此,这里我主要研究研究后两种。我们先简单分析下JSON和数据流各自的优缺点:
    JSON:
    优点:数据结构灵活,无需先制定复杂的协议;跨语言之间基本都有完整的解决方案。
    缺点:传送的数据因为要增加json的特征符(',"",:,{等),导致数据量较大;明文传输,无安全性可言。
    二进制数据流:
    优点:数据量小,无协议文档的话,较难破解内容。
    缺点:数据不灵活,需要先制定足够完善的协议文档。

    权衡利弊,在当前项目中,采用二进制数据流是更为合适的方案。

二、拆包和粘包
    我们下面来了解下python对于数据流通信这块的功能。根据TCP/IP通信的特点,不管是采用JSON字符串,还是二进制数据流,都必须面临的一个问题是:数据的拆包和粘包,具体的问题表现如下:
    采用如下代码,向socket服务器发送5000长度的数据,可以看到,在数据大于4000左右的时候被拆包,触发了服务端的2次dataReceived事件。

点击(此处)折叠或打开

  1. conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  2. conn.connect(("127.0.0.1", 5200))
  3. conn.send('abcdefghij'*500)


也即是说,在这里我们得粘包,保证数据完整后,才执行对应的逻辑。还有另外一种情况:客户端两次短暂的间隔时间内向服务端发送数据流时,TCP协议会将这2次数据合并为一次后,发送给服务端,如果遇到这样的情况,就需要我们对数据进行拆包处理。

三、常见的数据格式
    传输的数据中,常见的包括以下类型:字符串(长度可变),字节(1位),长整数(4位),无符号整形(4位),短整形(2位)。以上数据中,字符串是长度未定的,因此我们得使用一个短整型在字符串前,以便程序能知道后面的string占了几位。
    另外,为了计算封包的完整性,我们需要在封包首尾各指定一特殊的标示位。为了快速的读取数据,我们需要在封包头记录其长度。综上,我们制定了如下封包格式

点击(此处)折叠或打开

0x11 + 封包长度 + 协议号 + 数据 + 0x22


当中协议号int型(如:3290),如果数据是string,则保存为 short(str长度) + string(str内容)

四、python的二进制处理库
    有了封包格式,我们再来看下python中如何编码实现封包,这时,我们需要用到struct库,struct库主要有以下3个方法:

点击(此处)折叠或打开

  1. struct.pack(fmt,v1,v2,.....)

将v1,v2...等参数的值进行pack处理,pack的格式由fmt指定。被pack参数必须严格符合fmt。最后返回一个包装后的字符串。


点击(此处)折叠或打开

  1. struct.unpack(fmt,string)

顾名思义,解包,返回一个由解包数据(string)按照fmt格式解包后的元组(即使仅有一个数据也会被解包成远祖)。


点击(此处)折叠或打开

  1. struct.calcsize(fmt)

用来计算fmt格式所描述的结构的大小

    其中,fmt的格式所代表的含义和更多对struct的介绍,请浏览官方文档


五、我们的pack
    接下来,需要写一个方法,来按照我们的封包格式,对数据进行pack处理。

点击(此处)折叠或打开

  1. #coding:utf-8
  2. #基于python2.6
  3. '''
  4. 按照我的习惯,重写struct的格式化符为:
  5. s=字符串
  6. b=字节
  7. i=长整数
  8. u=无符号整形
  9. n=短整形
  10. '''
  11. datafmt = {
  12. '1001':'ssbi',
  13. #指定1001协议的数据格式,如果按如上指定,则表示该封包由
  14. #字符串 + 字符串 + 字节 + 长整型
  15. #构成
  16. }
  17. import struct
  18. #对数据进行pack数据 comid(string)=协议号 data(list)=数据内容
  19. def pack(comid,data):
  20. global datafmt
  21. if comid not in datafmt:
  22. print comid,'协议fmt格式读取错误'
  23. fmtStr = datafmt[comid]
  24. fmtStrRes = []
  25. idx = 0
  26. fixString ={}
  27. for k in fmtStr:
  28. if k=='n':
  29. fmtStrRes.append('h')
  30. elif k=='b':
  31. fmtStrRes.append('b')
  32. elif k=='u':
  33. fmtStrRes.append('I')
  34. elif k=='i':
  35. fmtStrRes.append('i')
  36. elif k=='s':
  37. _strLength = len(data[idx])
  38. fixString[idx] = _strLength #记录str的长度
  39. fmtStrRes.append('h'+ str(_strLength) +'s')
  40. idx = idx + 1
  41. fmt = '<'+''.join(fmtStrRes)
  42. #将字符串的长度值插入到data中
  43. if len(fixString)>0:
  44. idx = 0
  45. for k,v in fixString.items():
  46. data.insert(k+idx,v)
  47. idx = idx + 1
  48. print 'data=',data
  49. print 'fmt=',fmt
  50. res = struct.pack(fmt,*data)
  51. print 'pack=',res
  52. pack('1001',['abc','中文',3,7654])
运行看看:

至于unpack和拆包粘包,就交给你来自己练手了,应该没有难度了吧!

阅读(1977) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~