之前看到的一些博客主要分享的是linux环境下的c++和python的UDP通信,虽然有基础的可以很快的上手,但是对于小白来说用起来还是不太方便,所以在这里我再写一下win环境下的通信,附带源码。
本文的使用背景是,我使用C++搭建了一个飞机模型文件,但是飞机的航迹无法实时显示,给调试带来了困难。所以使用python中pygame库的画图功能,实时显示飞机位置,打印飞机航迹。但是C++的数据如何传输给python呢?这就用到了c++和python之间的UDP传输。(我深刻同意另一位博主的话,凡是和文件有关联的我都不喜欢)。
WIN下UDP发送
废话少说,先来看win下的代码,这里主要使用了两个函数,一个是UDPSend,另一个是UDPInfoSend。
UDPSend的功能就是调用UDP发送数据,UDPInfoSend是我用来写入需要发送数据的。没有耐心的小伙伴可以直接复制粘贴UDPSend使用,了解其使用方式就可以了,有兴趣的可以在附录里了解下原理。关于UDPSend的使用原理在下文会提到。
对了,不要忘了加入win下的socket头文件,他们是:
#include <time.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <vector>
#include <cstdio>
#include <stdio.h>
#include <iostream>
源代码如下:
void UDPSend(char* p, int size)
{
//创建套接字
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1, 1);
/*发送数据*/
char* SendData = (char*)malloc(sizeof(char) * size);
memcpy(SendData, p, size);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
return;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return;
}
SOCKET sockSen = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN addrSen;
addrSen.sin_family = AF_INET;//协议簇
addrSen.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSen.sin_port = htons(8888);
sendto(sockSen, SendData, strlen(SendData), 0, (SOCKADDR*)&addrSen, sizeof(SOCKADDR));
/*int WSAAPI sendto(
SOCKET s, //s 申请的通讯套接字
const char* buf, //* buf 要发送的缓冲区
int len, //len 发送的长度
int flags, //flags 调用的方式,一般写为 0
const sockaddr * to, //* to 目的地址指针
int tolen //tolen 地址结构长度
);*/
free(SendData);
SendData = NULL;
//关闭Socket对象
closesocket(sockSen);
WSACleanup();
}
void UDPInfoSend(PlaneModel* plane)
{
while (1)
{
int start, end, Head, test;
start = clock();
//将需要传递的内容转换为char
char buffer1[10], buffer2[10];
sprintf(buffer1, "%.8f", plane->PlaneInfo.Lati);
sprintf(buffer2, "%.8f", plane->PlaneInfo.Long);
char DataSend[20];
memcpy(DataSend, buffer1, 10);
memcpy(DataSend + 10, buffer2, 10);
UDPSend(DataSend, 20);
end = clock();
if ((end - start) < 40)
Sleep(40 - (end - start));
}
}
这里主要讲解UDPInfoSend怎么调用UDPSend,实现发送功能的。
第〇步,设置好UDP的传输地址("127.0.0.1")和端口(8888),主要是上述26/27行的内容。如果你也是c++到python的转换,直接复制源代码就行,不用理解。
首先,UDP传输的都是字节,而在传输之前,使用char 作为容器是最好的。
发送的第①步如下:将从C++向python发送所以需要将原本是double类型的飞机经纬度,转换成为文本文件。这里使用了cstdio库中的sprintf函数,具体解释如下。
sprintf(buffer1, "%.8f", plane->PlaneInfo.Lati);
//代表将plane->PlaneInfo.Lati保留八位小数,写入buffer1中
发送的第②步:将所需要发送的数据通过memcpy,拷贝到一个大的容器里,集中起来。
char DataSend[20];
memcpy(DataSend, buffer1, 10);//将buffer1拷贝到DataSend的1-10位
memcpy(DataSend + 10, buffer2, 10);//将buffer2拷贝到DataSend的11-20位
发送的第③步:将这个大的容器发送出去,调用DataSend
UDPSend(DataSend, 20);//前边是char*类型的数组,后边是数组的内有效数据的字节数,这里是20
这样一次UDP发送就完成了。
*建议UDP发送使用thread单独开一个线程去做,不要写在主函数里占用时间
Python下UDP接收
# udp 通信地址,IP+端口号
udp_addr = ('127.0.0.1', 8888)
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
udp_socket.bind(udp_addr)
while True:
recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数
# 打印接收到的数据
#print(f"[From {recv_data[1][0]}:{recv_data[1][1]}]:{recv_data[0]}")
json_data = {'latitude': float(recv_data[0][:10])*57.2958, 'longitude': float(recv_data[0][10:20])*57.2958}
Python下的操作一样,先要设置地址和端口号,确保两边地址和端口号一致。然后在收到数据后对数据进行处理,20位的数据前10位是lat,后10位是lon。我这里使用了json文件,方便后续的画图操作。
json_data = {'latitude': float(recv_data[0][:10]), 'longitude': float(recv_data[0][10:20])}
//folat是强制类型转换,recv_data[0]是收到的数据,recv_data[0][:10]是截取1-10位的数据,同理recv_data[0][10:20]就是截取10到20位的数据
*Python这里也可以单独开一个thread进行接收操作
最后的结果就是可以正常通信,并且可以实时画图显示
将飞机数据显示在图上。
附录