python中ctype的应用,协议解析,C语言与python的完美映射,结构体与字符串的相互转换

1.简论–写在前面

在做通信的过程中,经常要用到解析协议,平时用到python来写一些小的脚本,所以想用python来做一个协议解析的脚本。

从功能上来说,python在处理字串是很方便的,你可以将字符串转换位16进制字符串后,对每一个BYTE进行操作,结合pack和unpack模块,只是比较麻烦。但是如果用的ctype模块就很方便。

具体讲ctype模块的应用的文章已经很多了,但是在协议解析这块,有明确讲解字符串转换位结构体,和结构体到字符串的转换的很少。这里专门讲解结构体到字符串的转换。所以专门记录下怎么用CTYPE模块,进行字符串与结构的转换。

1.ctype介绍

在做协议之前,绕不开的话题是先要了解ctype模块。了解内容包括,数据类型和函数。
这里贴出官方讲解类容: Ctype官方说明.

1.1 ctype数据类型

数据类型其实和C语言里面都是一一对应的,我们只需要记录这么一个表,用之前查一下表即可。
在这里插入图片描述

1.2 ctype常用函数

下面先引用我接下来的例子需要用的函数的官方的说明,有理可依。

ctypes.addressof(obj)
   Returns the address of the memory buffer as integer. obj must be an instance of a ctypes type.

addressof函数,以整数形式返回一篇memory的地址。

ctypes.memmove(dst, src, count)
   Same as the standard C memmove library function: copies count bytes from src to dst. dst and src must be integers or ctypes instances that can be converted to pointers.

memmove函数,与标准的C memmove库函数相同,将count个字节,从src负值到dst。

ctypes.sizeof(obj_or_type)
   Returns the size in bytes of a ctypes type or instance memory buffer. Does the same as the C sizeof() function.

sizeof函数,与标准C sizeof()函数相同,都是返回ctype内省或实例缓存区大小,以字节位单位。

ctypes.string_at(address[, size])
   This function returns the string starting at memory address address. If size is specified, it is used as size, otherwise the string is assumed to be zero-terminated.

string_at函数,返回从内存地址address开始的字符串。如果指定了size,则将其用作size,否则假定字符串以零结尾。

有了上面的函数,我们就可以用来解析了。

2.C语言的结构体在python中的应用

需要解析的协议结构如下:
在这里插入图片描述
该协议为私有协议,协议固定头为固定的“6767”, 协议固定尾为固定的“5050”。

2.1 C 语言中字符串和结构体的转换

对于C语言来说,只需要定义一个结构体,如果要解析字符串,只需要利用C语言的多态性,将字符串指向字符串的地址,直接访问结构体既可以获取协议内容。同样的,如果需要输出这个协议的字符串,只需要申请一片内存,然后结构体指向这片内存,然后对结构体进行赋值。

由于协议里面含有可变长度,这里需要将上面的协议拆分位3个部分:协议头+协议体+协议尾。协议解析原理都是一样的,所以这里只讲解协议头的解析,也就是前面26个字节。

C语言中的结构体:

/** pro message header */
typedef struct _pro_msg_hdr
{
    /** pro message header */
    uint16_t     pro_fix_head;
    /** source mac */
    uint8_t       src_mac[6];
    /** target mac */
    uint8_t       target_mac[6];
    /** module id */
    uint16_t      module_id;
    /** crc check sum */
    uint16_t      pro_crc;
    /** msg id */
    uint32_t      msg_id;
    /** message length */
    uint32_t      msg_len;
} pro_msg_hdr_t;

有了结构体,分为2种情况,一种是有字符串解析协议体,第二种是生成字符串。

  1. 解析协议
uint8_t buf[] = [0x67, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x55, 0x5c, 0x00,
0x09, 0xbb, 0x02, 0x00, 0x6d, 0xe0, 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
 0x00, 0x14, 0x00, 0x00, 0x50, 0x50];

/* 定义一个结构体, 并将结构体指针指向字符串地址 */
pro_msg_hdr_t * pro_hdr = (pro_msg_hdr_t *)buf;
/* 直接访问,可以得到协议解析 */
print("module id:%d, msg id:%d\n", pro_hdr->module_id, pro_hdr_msg_id);
/* module id:2, msg id:2 */
  1. 生成字符串
/* 先申请一片内存 */
uint32_t size = sizeof(pro_msg_hdr_t)
void *buf= malloc(size);
if (buf == NULL) {
    print("alloc mem fail\n");
    assert(0)
}

/* 定义一个结构体, 并将结构体指针指向字符串地址 */
pro_msg_hdr_t * pro_hdr = (pro_msg_hdr_t *)buf;

/* 对结构体赋值*/
pro_hdr->module_id = 2;
pro_hdr->module_id = 2;
/* 输出字符串 */
print("string is :");
for (uint32_t i = 0; i < size; i++) {
    print("%02x, ", *(uint8_t *)(buf + i));
}
print("\n");

上面书写了C语言的利用结构体,解析字符串和生成字符串的方法。我想已经一目了然了。

下面讲解python怎么来实现C语言同样的功能。

2.2 python实现结构体和字符串的转换

python中是没有结构体的,所以如果要想实现如同C语言中的结构,可以用class来实现。这里直接给出来:


# 必须继承ctypes的Structure类
class ProMsgHdr(ctypes.Structure):
    _pack_ = 1  # 1字节对齐
    _fields_ = [('pro_fix_head', c_ushort)
                ('src_mac', c_ubyte * 6),
                ('dsr_mac', c_ubyte * 6),
                ('module_id', c_ushort),
                ('cli_crc', c_ushort),
                ('msg_id', c_uint),
                ('msg_len', c_uint)
        ]
        

注意:整个结构必须如同上面的格式一致!如果你不需要1字节对齐就去掉,或者几字节对齐就写几字节。

  1. 用python来解析字符串

buf = b"676700000000000048555c0009bb02006de0020000000500000001001400005050"
# 赋值
msg_hdr = ProMsgHdr()
# 得到类的长度
msghdrlen = sizeof(ProMsgHdr)

# 将16进制转换为字符串
str_bytes = binascii.unhexlify(buf)
# 将msg_hdr地址指向str_bytes
ctypes.memmove(ctypes.addressof(msg_hdr), str_bytes, msghdrlen)

print("module id:%d, msg id:%d" % (msg_hdr.module_id, msg_hdr.msg_id))

# module id:2, msg id:2
 
  1. 用python来生成字符串
# 得到类的实例
msg_hdr = ProMsgHdr()
# 赋值
msg_hdr.module_id = 2
msg_hdr.msg_id = 2
# 得到字符串
hdr_byte = ctypes.string_at(ctypes.addressof(msg_hdr), ctypes.sizeof(msg_hdr))

python用上面的方法就很方便的就可以解析字符串和生成字符串,不用再像以前,每次用pack和unpack来更具长度取字符。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值