1. 程式人生 > >用struct模組實現python socket收發自定義TCP包

用struct模組實現python socket收發自定義TCP包

最近跳槽到西安一家機器人公司,我們的產品屬於教育機器人的範疇,為了增強客戶吸引力,引進了一個智慧家居公司的產品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程式設計的標準