python构造复杂的C结构体(用于网络通信UDP、TCP)

 

       python提供了丰富的内置类型:比如字符串,整数,浮点数,列表,元组,字典,但是没有提供类似C语言的结构体类型,如果我们用python来实现客户端,用C来是实现服务器的话,需要用python来实现C的结构体,并且用UDP或者TCP传输。

        python中的struct模块专门用来处理python的值和C的类型结构之间的转换此模块执行的Python Python字符串表示C的结构之间转换这可以用来处理的二进制数据存储在文件或网络连接以及其他来源

  struct模块主要的方法有三种pack、unpack、calcsize:

  1.   struct.pack(fmt,v1,v2,.....) 将v1,v2等参数的值进行包装,包装的方法由fmt指定,被包装的参数必须严格符合fmt,最后返回字符串。
  2.         struct.unpack(fmt,string) 返回一个由解包数据(string)得到的一个元组(tuple)。
  3.   struct.calcsize(fmt),用来计算fmt格式所描述的结构的大小,也等于len(string)。

下面来介绍一下fmt(格式字符串格式):默认情况下,C类型的代表机器的原生格式字节顺序如果必要的(按C编译器使用规则跳过填充字节,正确对齐 此外,格式字符串的第一个字符,可以用来表明字节顺序,大小和压缩数据对齐根据下表:(默认情况是不加,实际上是按照@要求表示字节顺序以及字节,对齐)

Character Byte order Size Alignment
@ native native native
= native standard none
< little-endian standard none
> big-endian standard none
! network (= big-endian) standard none

 

 

格式字符串除了上表的第一个字符外,还支持一下格式:

Format C Type Python type Standard size Notes
x pad byte no value    
c char string of length 1 1  
b signed char integer 1 (3)
B unsigned char integer 1 (3)
? _Bool bool 1 (1)
h short integer 2 (3)
H unsigned short integer 2 (3)
i int integer 4 (3)
I unsigned int integer 4 (3)
l long integer 4 (3)
L unsigned long integer 4 (3)
q long long integer 8 (2), (3)
Q unsigned long long integer 8 (2), (3)
f float float 4 (4)
d double float 8 (4)
s char[] string    
p char[] string    
P void * integer   (5), (3)

例子:直接使用library上的例子

 >>> from struct import *
>>> pack('hhl', 1, 2, 3)  包装
'x00x01x00x02x00x00x00x03'
>>> unpack('hhl', 'x00x01x00x02x00x00x00x03')  解包
(1, 2, 3)
>>> calcsize('hhl')  
8
 

 最后来讲一下关键的问题:如何使用python来模拟C的结构体?

首先来看C的结构体如下面的形式:

struct info
{
int age;
 float score;
char name1[3];


};
typedef struct my_data
{
 char name[10];
 struct info test;
} MY_DATA;

如果用python来进行包装的话,可以使用下面的形式:

 name = "sleetdrop"
age = 28
score = 1000
name1="aes"

info = struct.pack("10sif3s", name,age, score, name1)

 

 最后看看用python写的客户端,用C写的服务器,用UDP传输信息。

 python代码:(ip地址和端口号根据自己需要更改)

import socket, struct
serviceip = "10.0.0.28"
port = 3000
name = "sleetdrop"
age = 28
score = 1000
name1="aes"
info = struct.pack("10sif3s", name,age, score, name1)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(info, (serviceip, port))
 

 C的代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


struct info
{
int age;
 float score;
char name1[3];


};
typedef struct my_data
{
 char name[10];
 struct info test;
} MY_DATA;

void dg_echo(int, struct sockaddr *, socklen_t);

int
main(int argc, char **argv)
{
 int                                   sockfd;
 struct sockaddr_in              svraddr, cliaddr;

 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
 bzero(&svraddr, sizeof(svraddr));
 svraddr.sin_family = AF_INET;
 svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
 svraddr.sin_port = htons(3000);

 bind(sockfd, (struct sockaddr *) &svraddr, sizeof(svraddr));
 dg_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));

}

void
dg_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen)
{
 int                  n;
 socklen_t         len;
 MY_DATA         mydata;

 bzero(&mydata, sizeof(MY_DATA));

 for( ; ; ) {
  len = clilen;
  n   = recvfrom(sockfd, &mydata, sizeof(MY_DATA), 0, pcliaddr, &len);
  printf("Name:%stAge:%dtScore:%fttest:%sn", mydata.name, mydata.test.age, mydata.test.score,mydata.test.name1);
 }}