UDP通信——使用python通过UDP通信来发送和解析数据

UDP通信——使用python通过UDP通信来发送和解析数据

经常我们要发送的信息是结构化的数据,此时发送和接收数据结构就是一个很基本的工作,怎样来实现呢?
发送和接收数据结构我们要用到 python 的 struct 模块,该模块可以用 struct来处理c语言中的结构体。

首先来看下struct中支持的数据类型

Format C Type Python 字节数
x pad byte no value 1
c char string of length 1 1
b signed char integer 1
B unsigned char integer 1
? _Bool bool 1
h short integer 2
H unsigned short integer 2
i int integer 4
I unsinged int integer or long 4
l long integer 4
L unsigned long long 4
q long long long 8
Q unsigned long long long 8
f float float 4
d double float 8
s char[] string 1
p char[] string 1
P void * long

struct模块中最重要的三个函数是pack(), unpack(), calcsize()

pack(fmt, v1, v2, …) 按照给定的格式(fmt),把数据封装成字符串(实际上是类似于c结构体的字节流)

unpack(fmt, string) 按照给定的格式(fmt)解析字节流string,返回解析出来的tuple(元组)

calcsize(fmt) 计算给定的格式(fmt)占用多少字节的内存

这里需要注意的是字节对齐的情况,字节对齐,简单来讲就是说比如一个 char 类型字符本来占1个字节,但是在结构体中,根据前后数据类型的不同,为了计算机读取内存的方便,会按照一定的规则给该 char 类型数据分配不只1个字节,可能是2个,那多出来的字节会导致我们按照各个数据类型长度算出来的与实际的不一样,我们可以写一段验证代码(下面是一段C++代码,因为用python来写的话还要用到一个库,下面会说到):

#include 

struct A
{
    double num1;
    double num2;
    bool num3;
};
#pragma pack(push,1)
struct B
{
    double num1;
    double num2;
    bool num3;
};
#pragma pack(pop)
int main()
{
    printf("length of A: %d\nlength of B: %d\n", sizeof(A), sizeof(B));
    return 0;
}

结果是:

length of A: 24
length of B: 17

1个double类型数据8个字节,1个bool类型数据1个字节,应该是17个字节,但是A的长度确实24个字节,相当于是3个double的长度,这就是字节对齐。而B的定义前后都加了一句话,

#pragma pack(push,1)     作用:是指把原来对齐方式设置压栈,并设新的对齐方式设置为一个字节对齐
#pragma pack(pop)          作用:恢复对齐状态

加上这两句话,就使得结构体的内存是连续的。
下面就可以用unpack()来进行数据解析了,要注意的是:字节对齐也有不同的规则,比如大端对齐、小端对齐等,所以需要struct用格式中的第一个字符来改变对齐方式。如下:

character byte order size aligenment
@ native native native
= native standard none
< little-endian standard none
> big-endian standard none
! network(= big-endian) standard none

用法就是在unpack(fmt, string)中的 fmt 加上符号,比如 ’<2i3d’表示将数据解析为小端对齐方式的2个int类型和3个double类型。

一、使用unpack()方式解析数据

发送端是c++代码,接收端是python。

发送端

#include 
#include 
#include 
#include 
#include 

#define PORT 8000

#pragma pack(push,1)  
struct B    //要发送的数据结构
{
    double num1;
    double num2;
    bool num3;
};
#pragma pack(pop)

int main(void)
{
    //实例化结构体,并赋值
    B message;  
    message.num1=1200;
    message.num2=3000;
    message.num3=1;
    //定义socket套字
    int sock;
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr)); //把servaddr内存清零
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    servaddr.sin_addr.s_addr = inet_addr("192.168.43.131");
    while (1)  
    {
        printf("向服务器发送:%.f, %.f, %d\n",message.num1,message.num2,message.num3);
        sendto(sock, &message, sizeof(B), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
        sleep(1);
    }
    close(sock);
    return 0;
}

接收端

# -*- coding: utf-8 -*-

import socket
import time
import struct

PORT = 8000
receiver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("192.168.43.131", PORT)
receiver_socket.bind(address)

while True:
        now = time.time()
        print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(now)))
        receive_message, client = receiver_socket.recvfrom(1024)
        data = struct.unpack('<2d1b',receive_message)
        print 'num1:' ,data[0], ' num2:',data[1], ' num3:',data[2]

结果如图:
UDP通信——使用python通过UDP通信来发送和解析数据_第1张图片

二、使用memmove操作内存解析数据

思路就是接收端定义一个与发送端相同的结构体,将socket接收的数据拷贝到结构体中。
需要用到 ctype 模块,ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C 动态链接库中的函数。
ctypes的类型对应如下:

ctypes type C type Python Type
c_char char 1-character string
c_wchar wchar_t 1-character unicode string
c_byte char int/long
c_ubyte unsigned char int/long
c_bool bool bool
c_short short int/long
c_ushort unsigned short int/long
c_int int int/long
c_uint unsigned int int/long
c_long long int/long
c_ulong unsigned long int/long
c_longlong __int64 or longlong int/long
c_ulonglong unsigned __int64 or unsigned long long int/long
c_float float float
c_double double float
c_longdouble long double float float
c_char_p char * string or None
c_wchar_p wchar_t * unicode or None
c_void_p void * int/long or None

对应的指针类型是在后面加上"_p",如int*是c_int_p等等。在python中要实现c语言中的结构,需要用到类。
下面是发送和接收的代码:

发送端 struct_sender.py

# -*- coding: utf-8 -*-

import socket
import time
import struct
from ctypes import *

class Data(Structure):
    _pack_ = 1   #让结构体内存连续
    _fields_ = [("member_1", c_ubyte),
                ("member_2", c_ubyte),
                ("member_3", c_float),
                ("member_4", c_float),
                ("member_5", c_double)]

data = Data()
data.member_1= 0xF2
data.member_2= 0x01
data.member_3= 125
data.member_4= 150
data.member_5= 1000

PORT = 8000
sender_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
receiver_address = ("192.168.43.131", PORT)

while True:
    start = time.time()
    print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start)))
    sender_socket.sendto(data, receiver_address)
    print 'member_1: %d' % data.member_1, 'member_2: %d' % data.member_2, 'member_3: %.2f' % data.member_3, \
          'member_4: %.2f' % data.member_4, 'member_5: %d' % data.member_5
    time.sleep(1)

接收端 struct_receiver.py

# -*- coding: utf-8 -*-

import socket
import time
import struct
from ctypes import *

class Data(Structure):
    _pack_ = 1
    _fields_ = [("member_1", c_ubyte),
                ("member_2", c_ubyte),
                ("member_3", c_float),
                ("member_4", c_float),
                ("member_5", c_double)]

data = Data()
PORT = 8000
receiver_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
address = ("192.168.43.131", PORT)
receiver_socket.bind(address)

while True:
        now = time.time()
        print(time.strftime('%Y-%m-%d %H:%M:%S',time.localtime(now)))
        message, client = receiver_socket.recvfrom(1024)
        memmove(addressof(data), (message), sizeof(Data))
        print 'member_1: %d' % data.member_1, 'member_2: %d' % data.member_2, 'member_3: %.2f' % data.member_3, \
              'member_4: %.2f' % data.member_4, 'member_5: %d' % data.member_5

在pycharm中运行,结果如图:
UDP通信——使用python通过UDP通信来发送和解析数据_第2张图片

你可能感兴趣的:(通信,python,UDP,数据结构,解析)