python之struct模块(网络数据包组包与解包)

1.在python中提供struct模块进行数据包的组装与解析,其中利用pack和unpack封装的方法进行数据包的拼装过程,实质上在数据包的拼装过程就是对应数据字节片状进行数据包的封装,解析过程是其你过程。具体情况如下所示:
struct模块中提供的使用方法是:

方法名                                  返回值                    功能描述
pack(formt,v1,v2…)                      string              按照给定的格式(formt),把数据转换成字符串(字节流),并将该字符串返回.
pack_into(formt,buffer,offset,v1,v2…)   None                按照给定的格式(formt),将数据转换成字符串(字节流),并将字节流写入以offset开始的buffer中.(buffer为可写的缓冲区,可用array模块)
unpack(formt,v1,v2…..)                  tuple               按照给定的格式(formt)解析字节流,并返回解析结果
pack_from(formt,buffer,offset)          tuple               按照给定的格式(formt)解析以offset开始的缓冲区,并返回解析结果
calcsize(formt)                         size of fmt         计算给定的格式(formt)占用多少字节的内存,注意对齐方式

2.如下所示format的格式字符表:
在这里插入图片描述
(图片来源网络)
3,使用举例:
组包:
在这里插入图片描述
如上如所示:在编辑字符串时必须使用bytes类型进行存储,否则会进行报错:(struct.error: argument for ‘s’ must be a bytes object的提示,所以在字符串之前使用b表示将字符串按照二进制bytes格式存储)
解包:
在这里插入图片描述
unpack根据对应的格式,将数据解析出来是一个元祖形式返回的。

以上两种方法在使用的时候,比较浪费内存空间,因为每进行一个pack的时候都会创建一快内存用来存储对应的数据包,在struct模块中提供另外一个模块ctypes可创建字节数据的形式来讲所有的需要组装的数据拼接成一个整体的大数据包,通过pack_into和unpack_from两个方法进行数据包的处理。如下举例,将连个字段按照tlv的格式进行组包后在进行j解析包,该方法可通过自定义协议数据包进行数据的发送。如下所示:

import struct
import ctypes
import binascii

def makePkt():
    #例如按照tlv格式三元组的形式存储几个字段数据
    name = (1,6,b'python')
    nas_ident = (2,4,b'xian')
    #创建一个内存区存储对应的数据字段
    buffer = ctypes.create_string_buffer(1024)
    offset = 0
    #name filed
    fmt = struct.Struct('II6s')
    struct.pack_into('II6s',buffer,offset,*name)
    offset += fmt.size
    #nas_ident
    fmt = struct.Struct('II4s')
    struct.pack_into('II4s',buffer,offset,*nas_ident)
    offset += fmt.size

    return binascii.hexlify(buffer) #转化成序列化编码

def parsePkt(pkt):
    unpkt = binascii.unhexlify(pkt)
    offset = 0
    #name
    fmt = struct.Struct('II6s')
    name_tag,name_len,name_value = struct.unpack_from('II6s',unpkt,offset)
    offset += fmt.size
    #nas_ident
    fmt = struct.Struct('II4s')
    nas_tag,nas_len,nas_value = struct.unpack_from('II4s',unpkt,offset)
    offset += fmt.size

    print(name_tag,'+',name_len,'+',name_value)
    print(nas_tag,'+',nas_len,'+',nas_value)

if __name__ =="__main__":
    pkt = makePkt()
    parsePkt(pkt)

运行结果:
在这里插入图片描述
注:在指定对应format的格式的时候,可能对于字符串的处理的数据长度示不固定的,可使用此种方式进行格式指定:
length = 5
sss = b’hello’
packstr = struct.pack(‘II%ss’ %length,1,2,sss)
print(packstr)
以上是struct模块使过程,在具体的失误中根据具体的情况进行制定格式组装数据包即可。
calcsize(format)方法:该方法计算对应的字节大小。
在这里插入图片描述
二,以上过程可以根据实际业务组装数据包。
今天在测试遇到一个问题,在c++中socket变成,在通信测试的时候,通过c++组装的数据包,我是用的是py写的客户端脚本进行测试。发现了一个问题,如上所示的在数据包组装完之后,使用了binascii中的序列化转化,进行发送,在c++的服务端发现收到的数据包存在差异,导致定义的规则协议,不能按照正常的规则解析出正确的数据格式。正确的使用方式,通过ctypes创建的缓冲区,使用struct模块进行数据包拼接完成之后,直接发送即可,这样保证在数据传输是避免py做的一些数据编码格式的转换。如果量端都是py编写的程序,是发送时使用binascii.hexlify进行格式化序列编码,接收端在进行反向binascii.unhexlify解序列化即可得到正确的数据包格式。

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值