用struct模組實現python socket收發自定義TCP包
阿新 • • 發佈:2019-01-11
最近跳槽到西安一家機器人公司,我們的產品屬於教育機器人的範疇,為了增強客戶吸引力,引進了一個智慧家居公司的產品API介面,讓機器人來操作智慧家居
該公司的智慧家居API是自定義TCP包,即直接在TCP頭後面寫自定義資料結構:
客戶端請求下載 傢俱資料庫 的格式
命令字(4位元組,小端) |
0x4c |
伺服器返回請求結果 的格式
命令字(4位元組,小端) | payload長度(4位元組,小端) | payload(N*1位元組) |
0x43 | 11262(尺寸很大) | sqlite資料庫 |
預設python socket只能收發字串,需要藉助struct才能收發二進位制資料
傳送請求
cmd_word = 0x4c tx_buf = struct.pack('<I', cmd_word) sock.sendall(tx_buf)
tx_buf據struct的文件說是其對輸入編碼生成的字串,用type(tx_buf)顯示確實是<type 'str'>,print tx_buf顯示字母L
但len(tx_buf) == 4, print repr(tx_buf)顯示
'L\x00\x00\x00'
也就是說len('L\x00\x00\x00') == 4
對於從C語言轉過來的人來說,上面情況真是理解不能,但它就是傳送成功了
接收應答
fp = open('house.db', 'wb+') recv_cnt = 0 while True: rx_buf = sock.recv(4096) len_buf = len(rx_buf) if len_buf ==0: break if recv_cnt == 0: cmd_word, data_len_total = struct.unpack(rx_buf[0:8]) buf = buffer(rx_buf, 8, len_buf - 8) fp.write(buf) else: buf = buffer(rx_buf) fp.write(buf) recv_cnt = recv_cnt +1
注意:
0、接收自定義幀頭時用unpack,可以獲得結構體各欄位取值
1、接收自定義幀內容(位元組流)時不用unpack,因為unpack返回的是tuple,而write不支援tuple型別
2、因為是二進位制寫入,所以必須將str轉成buffer型別
3、二進位制資料很大時,底層會拆分成多個乙太網幀,如果你sendall後馬上recv,則可能只收到1448位元組,不要擔心,這是因為你recv時核心協議棧只有一幀這麼多資料,它全部返回給你了,滿足socket程式設計的標準