Python 如何在另一个结构中解包一个C型结构?
我通过套接字接口从用C编写的应用程序(Python 如何在另一个结构中解包一个C型结构?,python,struct,unpack,Python,Struct,Unpack,我通过套接字接口从用C编写的应用程序(服务器)接收数据。发布的数据具有以下结构。我正在用Python编写的客户端接收数据 struct hdr { int-Id; char-PktType; 内部序号; int-Pktlength; }; 结构trl { 字符消息[16]; 长信息; }; 结构数据 { 字符值[10]; 双重结果; 长计数; 短int-valueid; }; 类型定义结构 { 结构hdr hdr_buf; 结构数据[100]; 结构trl trl_buf; }trx_单位;
服务器
)接收数据。发布的数据具有以下结构。我正在用Python编写的客户端接收数据
struct hdr
{
int-Id;
char-PktType;
内部序号;
int-Pktlength;
};
结构trl
{
字符消息[16];
长信息;
};
结构数据
{
字符值[10];
双重结果;
长计数;
短int-valueid;
};
类型定义结构
{
结构hdr hdr_buf;
结构数据[100];
结构trl trl_buf;
}trx_单位;
如何解压缩接收到的数据以访问我的内部数据缓冲区?库中有您所需的一切。库中有您所需的一切。使用库是一种方法。但是,您必须对序列化数据的C程序有更多的了解。考虑<代码> HDR < /代码>结构。如果C程序使用朴素方法发送:
struct hdr header;
send(sd, &hdr, sizeof(header), 0);
然后,客户端无法安全地解释发送给它的字节,因为在结构成员之间插入的填充量不确定。特别是,我希望在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);
}
这将确保您的客户可以使用以下模块安全解码:
如果无法修改C代码以修复序列化的不确定性,则必须从流中读取数据,找出C编译器插入填充的位置,并使用填充字节格式字符忽略填充值手动生成匹配的格式字符串
我通常用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)
请注意,这是未经测试的,可能包含语法错误,但这就是我解决问题的方法。使用该库是一种方法。但是,您必须对序列化数据的C程序有更多的了解。考虑<代码> HDR < /代码>结构。如果C程序使用朴素方法发送:
struct hdr header;
send(sd, &hdr, sizeof(header), 0);
然后,客户端无法安全地解释发送给它的字节,因为在结构成员之间插入的填充量不确定。特别是,我希望在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);
}
这将确保您的客户可以使用以下模块安全解码:
如果无法修改C代码以修复序列化的不确定性,则必须从流中读取数据,找出C编译器插入填充的位置,并使用填充字节格式字符忽略填充值手动生成匹配的格式字符串
我通常用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)
请注意,这是未经测试的,可能包含语法错误,但这就是我解决问题的方法。您是通过套接字接收字节串吗?@egaio是的。我是通过套接字接收字节串。您是通过套接字接收字节串吗?@egaio是的。我是通过套接字接收字节串。@Shawley感谢您的指导。你能详细介绍一下10sdIIH和16sII吗?@Soumajit-我添加了一些注释和文档字符串。我还转换为对64位有符号值使用
q
。我错误地认为它只在本机模式下可用。请参见@Shawley的注释(2),谢谢您的指导。你能详细介绍一下10sdIIH和16sII吗?@Soumajit-我添加了一些注释和文档字符串。我还转换为对64位有符号值使用q
。我错误地认为它只在本机模式下可用。参见@Equalio的注释(2),谢谢您的建议。我不确定unpack
@equalio中的结构的格式说明符,谢谢你的建议。我不确定unpack