如何优雅地在Python中发送/接收一个大型C结构体?
我开始写一个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
你可以写一个类,这个类里面有一些函数,用来把数据打包到类的属性里,或者从类的属性里解包数据,使用像 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