如何优雅地在Python中发送/接收一个大型C结构体?

3 投票
3 回答
5011 浏览
提问于 2025-04-16 17:50

我开始写一个Python 3.x的客户端应用程序。服务器应用程序已经存在,是用C语言写的。服务器提供了一个C语言的头文件,里面定义了两个结构体,用来通过UDP发送和接收数据(我使用的是Python的socket模块)。

问题是,这些C语言的结构体比较大(每个大约有200个元素)。如果我用Python的struct模块来打包和解包数据,那就得手动处理这200个元素,这样的做法显得不太优雅,比如:

struct.pack('H...I', data1, ..., data200)

此外,我希望在Python中能够用类似C语言的语法来访问接收到或发送的元素。例如,如果我在C语言的服务器端这样做:

send.data.pos = pos;

那么在Python客户端中,能够像这样访问pos变量就会显得很自然:

pos = recv.data.pos

需要注意的是,我的问题不是如何从头文件自动生成Python中的结构体,像在这个讨论中那样(我可以逐个在Python中写出每个结构体字段),而是如何在Python中组织这些数据(例如,使用类、字典等),以便利用Python的特性,让代码更简单,数据更容易访问(我希望只使用Python的标准模块,不用外部软件)。那么,最优雅的实现方式是什么呢?

3 个回答

1

你可以试试 dpkt,这是一种简单的方法来访问数据包的信息。想了解怎么用,可以看看 这里,里面有使用的例子。下面是一个简单的例子:

class Foo(dpkt.Packet):
    __hdr__ = (('type', 'B', 0),
               ('size', 'B', 0))

data = get_udp_message()
foo = Foo(data)
if foo.size != len(data):
    print "Bad size in header"
if foo.type == 3:
    parse_payload(foo.data)
1

你可以写一个类,这个类里面有一些函数,用来把数据打包到类的属性里,或者从类的属性里解包数据,使用像 struct.pack 这样的工具。

我建议你看看 Construct。不过我觉得它还没有移植到 Python 3.x 上。Construct 之前有一段时间没有更新,但最近被一个新开发者接手了,所以也许很快就能支持 Python 3.x 了。

1

试试这个——在2.7和3.2版本上都能用。

脚本:

import struct, collections

class CStruct(object):

    def __init__(self, typename, format_defn, lead_char="!"):
        self.names = []
        fmts = [lead_char]
        for line in format_defn.splitlines():
            name, fmt = line.split()
            self.names.append(name)
            fmts.append(fmt)
        self.formatstr = ''.join(fmts)
        self.struct = struct.Struct(self.formatstr)
        self.named_tuple_class = collections.namedtuple(typename, self.names)

    def object_from_bytes(self, byte_str):
        atuple = self.struct.unpack(byte_str)
        return self.named_tuple_class._make(atuple)

if __name__ == "__main__":
    # do this once
    pkt_def = """\
        u1 B
        u2 H
        u4 I"""
    cs = CStruct("Packet1", pkt_def)
    # do this once per incoming packet
    o = cs.object_from_bytes(b"\xF1\x00\xF2\x00\x00\x00\xF4")
    print(o)
    print(o.u4)

输出:

Packet1(u1=241, u2=242, u4=244)
244

撰写回答