由于最近很多人在询问如何模拟客户端给服务端发送协议,解析服务端返回的内容,对struct这个模块还有问题,我以直白的语言描述和拿自己的测试案例进行一个演示,基础可以查看基础可参考:https://my.oschina.net/u/4521128/blog/4388911
1.struct它是干什么的
官方解释是:在Python值和C结构之间转换的函数。 Python bytes对象用于保存表示C结构的数据
直白一点,在c语言中c语言包含不同类型的数据(int,char,bool等等),方便对某一结构对象进行处理。而在网络通信当中,大多传递的数据是以二进制流(binary data)存在的。当传递字符串时,那你就需要有一种机制将某些特定的结构体类型打包成二进制流的字符串然后再网络传输,而接收端也应该可以通过某种机制进行解包还原出原始的结构体数据。python中的struct模块就提供了这样的机制,该模块的主要作用就是对python基本类型值与用python字符串格式表示的C struct类型间的转化,以下原话来自https://www.cnblogs.com/coser/archive/2011/12/17/2291160.html
总结一句:struct作用它就是转换成特定格式用于在网络上传输
2.struct如何使用
案例:客户端需要给服务端发送一条指令,然后客户端解析收到服务端给的指令是什么 比如来一个上报的协议,
看上面的协议,我们需要跟进这个协议给模拟客户端去发送请求,然后得到服务端的返回,解析这串数据,那我们该如何写代码(注:regid与rom都是包体,而Version与Version是包头)
1.创建一个scoket连接
2.进行协议的拼接,打包
3.解析协议,解包
2.1代码编写
#创建sock连接 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #进行协议的拼接 字符串长度 fmt = '!H%dsB' % (len(rid))print ('|rid report send body|rid:{0}|len(rid):{1}|rom:{2}|'.format(rid,len(rid),rom)) BODY = struct.pack(fmt,len(rid),rid,rom)
我们拼接协议的字符串长度,也就是根据协议来的,看上面的图,我们需要在传递4个参数,而且参数都是类型的,3个int 1个不定长,我们可以理解为字符串,既然服务端需要这些数据,那我们给它这些数据,而我们要在网络上传输,那么我们就需要转换成字节,那如何转,看下面,比如协议中 Version是一个int型的大小1B的内容,那么编码就可以是B,b,但是这个头部,我们看regid这个不定长,由于不定长,实际包含了数字类型和字符串类型,所以它的格式是H,在加上s,s就是对应了string,为什么会有格式%d,是因为不定长,长度实际是传入的长度, 我们在看类型的值,类型是个interget,大小1b,所以我们可以使用B或者b,格式符 然后我们通过stuck.pack进行打包,以下是对应关系
实际过程 :在代码中还需要对头部文件+主体在进行加密,以下是包头信息
所以实际我们打包看到的数据是这样的
#调用的方法 主体+包头 response_login= Send_tcp_aes(27,BODY,sock,uid,Rid,sid,ver,Encryption=Encryption,algorithm=algorithm,reserve=reserve)
#方法中关键头部和主体的内容
PKG_HEAD = struct.pack("!HbBBBIHIQ",LEN,Version,Command,algorithm,0,0,RID,SID,UID)
PKG=PKG_HEAD+body_secret
#发送我们的数据
sock.send(PKG)
当我们发出的数据,实际是这样二进制数据
b'\x00C\x01\x1b\x00\x00\x00\x00\x00\x00\xb4\xf8\x00\x00\x00\x18\x00\x00\x00\x01\xdeF\xd3\x07\x00(RidReport0000000000000000000+=XpRge4Mkwc\x02'
2.2解析数据
从服务端返回的字节数也是这样的二进制字节数据
b'\x00\x16\x01\x1b\x00\x00\x00\x00\x00\x00\xbe\xf5\x00\x00\x00\x01\xdeF\xd3\x07\x00\x00'
解析也就是根据协议进行解析,正常来说会有一个解析格式的,但是也很奇怪,这边协议脚本解析完头部数据后,直接使用的">H"就可以进行解析了,目前这块也是没有找到解析的协议文档
#解析主体部分
而data就是数据源d=b'\x00\x00' errcode_tmp = struct.unpack('>H', data[:2])
解析出来的响应数据,为0就是返回正常
3.需要注意的坑
如果你按协议去转换字节格式时,发现在怎样都不对,那可能要怀疑服务端并不是接收这个协议的,举个案例,这个协议最后的一个字段是个4的大小的inte型,那么转换格式一个i就行,但是如果一旦你出现i,就会报字段大小不合适的问题,之后不行,就尝试调到了最后字段的长度,发现就对了。