由於最近很多人在詢問如何模擬客戶端給服務端發送協議,解析服務端返回的內容,對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,就會報字段大小不合適的問題,之后不行,就嘗試調到了最后字段的長度,發現就對了。