在Python中序列化C结构并通过套接字发送

ulydmbyx  于 2023-04-29  发布在  Python
关注(0)|答案(3)|浏览(188)

我正在尝试序列化下面的C结构

struct packet
{
    int id;
    unsigned char *ce;
    unsigned char *syms;
};

然后通过socket发送。cesyms所指向的元素的数量已知为N。目前我是这样做的。首先,我使用ctypes将结构体 Package 为

class Packet(Structure):
    _fields_ = [("id",  c_int),
                ("ce", POINTER(c_ubyte)),
                ("syms", POINTER(c_ubyte))]

然后,我使用ctypes.POINTER(Packet)中的fill_data函数填充以下类的对象:

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并填充一个单独的类示例。谢谢。

h4cxqtbf

h4cxqtbf1#

最后,我想出了下面的方法来手动序列化“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()函数是关键。

sr4lhrrt

sr4lhrrt2#

首先,如果你知道元素的个数是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))
yptwkmov

yptwkmov3#

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())

相关问题