1. 程式人生 > >Python使用struct模組轉換C語言結構體,打包、解包二進位制資料

Python使用struct模組轉換C語言結構體,打包、解包二進位制資料

本文是我使用socket測試網路介面寫的訊息頭,其中包含以下內容:
(1)通過Python的struct模組將C的結構體轉換成Python語言(struct.Struct)
(2)打包和解包(pack_into和unpack_from)
(3)序列化和反序列化(SerializeToString和ParseFromString)

C語言的訊息頭如下,是個C的結構體:

#pragma pack(1)
struct PduHead{
         unsigned int   flag; 
         unsigned short packet_len;
         unsigned int   cmd;
         unsigned char  version;
         unsigned char  reserve[1];
         unsigned char  body[0];
};
#pragma pack()

將C語言的訊息頭轉換為Python類,其中reserve[1]是保留字,不用管,body[0]是個長度為0的陣列,儲存body的首地址,也不用管。轉換後如下

import struct
import ctypes

class PduHead(object):
    def __init__(self, cmdtype=0):
        self.flag = GLOBAL_SIGNAL_FLAG
        self.packet_len = 0
        self.cmd = cmdtype
        self.version = VERSION
        # struct.Struct返回一個新的Struct物件,根據傳入的位元組順序字串寫入和讀取二進位制資料
        # “!IHIcc”表示二進位制資料的位元組順序
        # “!”表示位元組順序為network
        # “IHIcc”分別是根據以上幾個引數的型別得到的格式字元
        # 二進位制流會根據這個位元組順序去解析          
        self.struct = struct.Struct('!IHIcc')

    # 序列化函式
    def SerializeToStringWithMsg(self, req_msg):
        if not req_msg:
            logger.error('No request message set!')
            return ''
        body_str = req_msg.SerializeToString()  # 序列化,將物件轉化為可傳輸的二進位制流
        values = (self.flag, len(body_str) + self.struct.size, self.cmd, self.version, '\0')
        buffer = ctypes.create_string_buffer(self.struct.size) # 建立一個buffer
        self.struct.pack_into(buffer, 0, *values)  # 根據位元組序打包,將打包的位元組寫入到buffer中,values元組表示要寫入的值
        # buffer物件提供了raw屬性訪問當前buffer內容
        # 該方法返回一個要傳輸的訊息頭和訊息體的二進位制流
        return buffer.raw + body_str 

    # 反序列化函式
    def ParseFromStringWithMsg(self, data):
        # 根據位元組序解析二進位制流,返回一個元素,形似SerializeToStringWithMsg方法中的value,所以self.cmd是元組的第三個元素
        self.cmd = self.struct.unpack_from(data[:self.struct.size])[2]  
        resp_msg = dict_message.get(self.cmd)  # 根據self.cmd得到響應資料的二進位制流(這是個不用關心,跟業務有關)
        if not resp_msg:
            logger.error('Message [%d] Not Found!' % self.cmd)
            return None
        resp_msg = resp_msg()
        if resp_msg:
            resp_msg.ParseFromString(data[self.struct.size:])  # 反序列化,將二進位制流轉化為實際的物件
        return [self.cmd, resp_msg]

位元組順序:
1、位元組順序的第一個字元可用於指示打包資料的位元組順序、大小和對其方式這裡寫圖片描述

2、格式字元
C和Python之間的型別轉換,組合的字串表示二進位制資料的位元組順序,如上面程式中的“!IHIcc”
這裡寫圖片描述