如何在另一个结构中解压缩C风格的结构?

时间:2017-03-14 11:51:47

标签: python struct unpack

我通过套接字接口从用C编写的应用程序(server)接收数据。发布的数据具有以下结构。我正在用Python编写的客户端接收数据。

struct hdr
{
   int  Id;
   char PktType;
   int  SeqNo;
   int  Pktlength;
};

struct trl
{
     char Message[16];
     long long info;
};

struct data
{
    char value[10];
    double result;
    long long count;
    short int valueid;
};

 typedef struct
 {
     struct hdr hdr_buf;
     struct data data_buf[100];
     struct trl trl_buf;
 } trx_unit;

我如何unpack收到的数据来访问我的内部数据缓冲区?

2 个答案:

答案 0 :(得分:1)

struct库可以满足您的所有需求。

答案 1 :(得分:0)

使用struct库是可行的方法。但是,您必须更多地了解序列化数据的C程序。考虑hdr结构。如果C程序使用天真的方法发送它:

struct hdr header;
send(sd, &hdr, sizeof(header), 0);

然后您的客户端无法安全地解释发送给它的字节,因为在struct成员之间插入了不确定的填充量。特别是,我希望在PktType成员之后填充三个字节的填充。

处理二进制数据发送的最安全方法是让服务器和客户端直接序列化字节,以确保没有额外的填充并使多字节整数的字节顺序显式化。例如:

/*
 * Send a header over a socket.
 *
 * The header is sent as a stream of packed bytes with
 * integers in "network" byte order.  For example, a
 * header value of:
 *   Id: 0x11223344
 *   PktType: 0xff
 *   SeqNo: 0x55667788
 *   PktLength: 0x99aabbcc
 * 
 * is sent as the following byte stream:
 *   11 22 33 44 ff 55 66 77 88 99 aa bb cc
 */
void
send_header(int sd, struct hdr const* header)
{   /* NO ERROR HANDLING */
    uint32_t num = htonl((uint32_t)header->Id);
    send(sd, &num, sizeof(num), 0);
    send(sd, &header->PktType, sizeof(header->PktType), 0);
    num = htonl((uint32_t)header->SeqNo);
    send(sd, &num, sizeof(num), 0);
    num = htonl((uint32_t)header->PktLength);
    send(sd, &num, sizeof(num), 0);
}

这将确保您的客户端可以使用struct模块安全地解码它:

buf = s.recv(13)  # packed data is 13 bytes long
id_, pkt_type, seq_no, pkt_length = struct.unpack('>IBII', buf)

如果您无法修改C代码以修复序列化不确定性,那么您将必须从流中读取数据并找出C编译器插入填充的位置并手动构建struct格式字符串以匹配使用填充字节格式字符,用于忽略填充值。

我通常在Python中编写一个解码器类,从套接字读取一个完整的值。在你的情况下,它看起来像:

class PacketReader(object):
    def __init__(self, sd):
        self._socket = sd

    def read_packet(self):
        id_, pkt_type, seq_no, pkt_length = self._read_header()
        data_bufs = [self._read_data_buf() for _ in range(0, 100)]
        message, info = self._read_trl()
        return {'id': id_, 'pkt_type': pkt_type, 'seq_no': seq_no,
                'data_bufs': data_bufs, 'message': message,
                'info': info}

    def _read_header(self):
        """
        Read and unpack a ``hdr`` structure.

        :returns: a :class:`tuple` of the header data values
            in order - *Id*, *PktType*, *SeqNo*, and *PktLength*

        The header is assumed to be packed as 13 bytes with
        integers in network byte order.

        """
        buf = self._socket.read(13)
        # >   Multibyte values in network order
        # I   Id as 32-bit unsigned integer value
        # B   PktType as 8-bit unsigned integer value
        # I   SeqNo as 32-bit unsigned integer value
        # I   PktLength as 32-bit unsigned integer value
        return struct.unpack('>IBII', buf)

    def _read_data_buf(self):
        """
        Read and unpack a single ``data`` structure.

        :returns: a :class:`tuple` of data values in order -
            *value*, *result*, *count*, and *value*

        The data structure is assumed to be packed as 28 bytes
        with integers in network byte order and doubles encoded
        as IEEE 754 binary64 in network byte order.

        """
        buf = self._socket.read(28)  # assumes double is binary64
        # >   Multibyte values in network order
        # 10s value bytes
        # d   result encoded as IEEE 754 binary64 value
        # q   count encoded as a 64-bit signed integer
        # H   valueid as a 16-bit unsigned integer value
        return struct.unpack('>10sdqH', buf)

    def _read_trl(self):
        """
        Read and unpack a ``trl`` structure.

        :returns: a :class:`tuple` of trl values in order -
            *Message* as byte string, *info*

        The structure is assumed to be packed as 24 bytes with
        integers in network byte order.

        """
        buf = self.socket.read(24)
        # >   Multibyte values in network order
        # 16s message bytes
        # q   info encoded as a 64-bit signed value
        return struct.unpack('>16sq', buf)

请注意,这是未经测试的,可能包含语法错误,但这就是我如何解决问题。