我正在尝试序列化以下 C 结构
struct packet
{
int id;
unsigned char *ce;
unsigned char *syms;
};
在 Python 中并通过套接字发送。
ce
和syms
指向的元素个数已知为N
。目前我正在这样做。首先,我使用 ctypes 将结构包装为
class Packet(Structure):
_fields_ = [("id", c_int),
("ce", POINTER(c_ubyte)),
("syms", POINTER(c_ubyte))]
然后,我使用
fill_data
中的 ctypes.POINTER(Packet)
函数填充以下类的对象:
class DataLoad:
def __init__(self):
self.id = -1
self.ce = []
self.syms = []
def fill_data(self, pkt_p):
""" pkt_p is POINTER(Packet)
"""
self.id = pkt_p.contents.id
self.ce = []
for i in range(N):
self.ce.append(pkt_p.contents.ce[i])
self.syms = []
for i in range(N):
self.syms.append(pkt_p.contents.syms[i])
最后我干脆用
pickle.dumps(DataLoad)
生成字节流发送了
这种方法效果很好。但是,它似乎很慢。我能看到的一个原因是
pickle.dumps
带来很多开销。例如,如果 C 结构只有 1024 个字节,我可能必须使用 pickle 为每个结构发送近 4000 个字节。此外,打包/填充 DataLoad 也需要时间。
所以我的问题是,我是否有其他更好的选择来在 python 中序列化此 C 结构并发送?我宁愿避免 pickle 并填充一个单独的类实例。谢谢。
最后我想出了以下方法来手动序列化 `Packet' 实例而不使用 pickle。
def serialize(pkt_p, size_g, size_p):
""" Serialize Packet instance
size_g - number of elements pointed by ce
size_p - number of elements pointed by syms
Return a byte stream
"""
pktstr = b''
pktstr += struct.pack('i', pkt_p.contents.id)
pktstr += string_at(pkt_p.contents.ce, size_g)
pktstr += string_at(pkt_p.contents.syms, size_p)
return pktstr
def deserialize(pkt_p, pktstr, size_g, size_p):
""" De-serialize pktstr and fill a POINTER(Packet)
"""
pkt_p.contents.id = struct.unpack('i', pktstr[0:4])[0]
ce = (c_ubyte * size_g).from_buffer_copy(pktstr[4:4+size_g])
pkt_p.contents.ce = cast(ce, POINTER(c_ubyte))
syms = (c_ubyte * size_p).from_buffer_copy(pktstr[-size_p:])
pkt_p.contents.syms = cast(syms, POINTER(c_ubyte))
string_at()
和from_buffer_copy()
功能是关键。
首先,如果您知道元素的数量为 N,我建议您将结构更改为:
class Packet(Structure):
_fields_ = [("id", c_int),
("ce", c_ubyte * N),
("syms", c_ubyte * N)]
接下来,如果你只想发送结构数据,你不需要 pickle 整个东西。只需发送数据包:
p = Packet()
p.id = 555
...
# cast the struct to a pointer to a char array
pdata = ctypes.cast(ctypes.byref(p), ctypes.POINTER(ctypes.c_char * ctypes.sizeof(p)))
# now you can just save/send the struct data
someSocketObject.send(pdata.contents.raw)
读取对方数据:
p = Packet()
raw = someSocketObject.read(ctypes.sizeof(p))
ctypes.memmove(ctypes.pointer(p),raw,ctypes.sizeof(p))
Packer 是一个用于序列化和反序列化数据的存储库,适用于 python 和 C。
https://github.com/souzomain/Packer
from Packer import Packer, Parser
packer = Packer()
packer.add_str("hello world")
print(f"packet size: {packer.get_size()} | packet: {packer.get_buffer()}")
parser = Parser(packer.get_buffer(), packer.get_size())
print(parser.parse_str())