python 与C++ 利用socket实现json数据传输
单机 python 与C++ 程序 利用socket实现json数据传输
需求
将C++环境中的数据发送到python ,python进行处理后将结果返回给C++
因此需要实现python与C++代码通信
实现方法的选择
1.实现方法
(1)C/C++里调用Python脚本函数
Python与C/C++混合编程
优点:实现起来最简单,稳定,可靠
缺点:迁移运行环境后,需要安装重新部署Python脚本的Python运行环境,非常麻烦。
(2)将Python打包成独立进程,C/C++于Python实现进程间通信,有以下几种方式:
(1)SOCKET通信
(2)管道
(3)共享文件
(4)共享内存
最终选择使用socket通信,通信的字节流格式设置为json格式
具体实现
流程图示
涉及到的技术
1 socket 通信
使用到的函数
C++ 客户端
0 网络库与头文件
函数解析:socket编程:网络库与网络头文件_超级大洋葱806的博客-CSDN博客_socket库文件
#include <Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
1 WSAStartup()
函数解析:socket编程:WSAStartup函数详解_超级大洋葱806的博客-CSDN博客_wsastartup
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1, 1);
int err;
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1) {
WSACleanup();
return -1;
}
2 socket()
函数解析 : socket编程:socket()函数详解_超级大洋葱806的博客-CSDN博客_socket()
// 设置socket
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (sockClient == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //地址
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888); //端口
3 connect() 建立链接
函数解析 : socket编程:服务端和客户端编程流程、connect()函数_超级大洋葱806的博客-CSDN博客
//建立链接
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//sockClient:客户端的套接字(表明即将发起连接请求),
//第二个参数是服务端的套接字所在的“地方”(“地方”是我自定义的专有名词),
//第三个参数是该“地方”的大小。
//如果请求连接成功,则返回0,否则返回错误码。
4 send() 发送
函数解析: socket编程:send()函数的使用和本质_超级大洋葱806的博客-CSDN博客_socket的send函数
//发送
send(sockClient, sendData, strlen(sendData) + 1, 0);
//sockClient:用来传输数据的socket描述符; msg:一个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0
//成功返回写入的字节数 执行失败,返回SOCKET_ERROR
5 recv() 接收
函数解析: socket编程:recv()函数详解_超级大洋葱806的博客-CSDN博客_socket.recv
//接收
string str = "";
char* receiveData = NULL;
int ret = 0;
char recvBuf[500]; //缓存区 ,用于存放python发送的数据
ret = recv(sockClient, recvBuf, 500, 0); //接受数据
//sockClient:接受数据的socket描述符;recvBuf 是存放接收数据的缓冲区 ; len是缓冲的长度;Flags也被置为0
// 返回值为消息长度
if (ret > 0) {
printf("接受到的数据");
printf("%s\n", recvBuf);
recvBuf[ret] = 0x00;
}
6 关闭链接
函数解析:
closesocket(sockClient);
WSACleanup();
Python 服务端
0 头文件
import socket
1 socket()
函数解析: Python 网络编程 | 菜鸟教程 (runoob.com)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2 bind() 端口绑定
函数解析:
socket编程:bind()函数详解_超级大洋葱806的博客-CSDN博客_socket bind
server.bind(("localhost",8888))
3 listen() 等待链接
函数解析: socket编程:listen()函数详解_超级大洋葱806的博客-CSDN博客_socket.listen
server.listen(0)
#backlog指定最多允许多少个客户连接到服务器。
#backlog应该理解为阻塞队列的长度,总共与服务器连接的客户端一共有 backlog + 1 个。
#收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
4 accept() 允许链接
函数解析: accept函数_Python Socket函数详解_weixin_39812465的博客-CSDN博客
connection , address = server.accept()
#返回值 connection是新的套接字对象,可以用来接受和发送数据
5 recv() 接收数据
recv_data = connection.recv(1024).decode('UTF-8', 'ignore').strip(b'\x00'.decode())
# 接受套接字的数据,数据以字符串的形式返回
6send() 发送数据
connection.send(send_data)
7 关闭链接
connection.close()
2 json 格式数据的编码与解析
C++ 端:
0 额外的包的使用与选择 cJSON
使用Cjson 进行json格式文件的打包 与解压
(38条消息) 【嵌入式开源库】cJSON的使用,高效精简的json解析库_凉开水白菜的博客-CSDN博客_cjson解析bool
GitHub - DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C
使用方法也很简单,讲h文件和c文件放到项目中即可
1 创建JSON对象 cJSON_CreateObject() ,并将所有对象放在一个链表上 cJSON_AddNumberToObject
/* print the version */
printf("Version: %s\n", cJSON_Version());
/* Now some samplecode for building objects concisely: */
//create_objects(); 创建JSON对象
cJSON* cjson_test = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* sendData = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cjson_test, "name", "mculover666");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
//cJSON_AddNumberToObject(cjson_test, "age", 22);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
cJSON_AddItemToObject(cjson_test, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("C"));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("Java"));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("Python"));
cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cjson_test, "student");
2 打印JSON对象 cJSON_Print()
/* 打印JSON对象(整条链表)的所有数据 */
sendData = cJSON_Print(cjson_test);
printf("%s\n", sendData);
3 解析JSON对象 cJSON_Parse()
cJSON* cjson_receive = cJSON_Parse(recvBuf);
4 获取cJSON对象里的元素 cJSON_GetObjectItem cJSON_GetNumberValue
//获取JSON对象里的元素
cJSON * fStickLat = cJSON_GetObjectItem(cjson_receive, "fStickLat");
double m_fStickLat = cJSON_GetNumberValue(fStickLat);
printf("%.10f\n", m_fStickLat);
printf("m_fStickLat: %.10f\n", cJSON_GetNumberValue(fStickLat));
5 cJSON对象的销毁cJSON_Delete()
cJSON_Delete(cjson_test);
Python 端口
后将json保存到对象中/或者直接从json格式中读取内容,python可以类似访问字典格式一样访问json数据
python 打包json文件并发送到C++
使用Python读取和解析JSON数据教程 - 知乎 (zhihu.com)
0 选择的包
1 解析json数据 json.loads()
在python端接受到数据后发现并不能直接调用json进行解析,通过搜索找到了解决方法
(39条消息) python 去除不可见字符 \x00_linglongbayinhe的博客-CSDN博客
recv_data = connection.recv(1024).decode('UTF-8', 'ignore').strip(b'\x00'.decode())
data_json = json.loads(recv_data)
2 转换json数据 json.dumps()
#将数据打包为json
FlyCtrlCmd={
"fStickLat": 0.5,
"fStickLon": 0.5,
"fThrottle": 0.5,
"m_fRudder": 0.5,
}
FlyCtrlCmd_json = json.dumps(FlyCtrlCmd)
print("发送的数据")
print(FlyCtrlCmd_json)
3获取JSON对象里的元素
print(data_json['name'])
代码
python 服务端
import socket
import time
import json
if __name__ == '__main__':
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#
server.bind(("localhost",8888))
#
server.listen(0)
#backlog指定最多允许多少个客户连接到服务器。
#backlog应该理解为阻塞队列的长度,总共与服务器连接的客户端一共有 backlog + 1 个。
#收到连接请求后,这些请求需要排队,如果队列满,就拒绝请求
connection , address = server.accept()
#
print(connection ,address)
num = 0
while True:
recv_data = connection.recv(1024).decode('UTF-8', 'ignore').strip(b'\x00'.decode())
if not recv_data:
break
num = num + 1
print("接受到的数据")
print(recv_data)
#解析接收到的json格式的文件
data_json = json.loads(recv_data)
print("解析后的数据")
print(data_json)
print(data_json['name'])
#打包文件发送到C++环境
#将数据打包为json
FlyCtrlCmd={
"fStickLat": 0.5,
"fStickLon": 0.5,
"fThrottle": 0.5,
"m_fRudder": 0.5,
}
FlyCtrlCmd_json = json.dumps(FlyCtrlCmd)
print("发送的数据")
print(FlyCtrlCmd_json)
#发送数据
#将数据由str编码为bytes字节流 encode()
FlyCtrlCmd_json_bytes = FlyCtrlCmd_json.encode('UTF-8', 'ignore')
connection.send(FlyCtrlCmd_json_bytes)
time.sleep(0.5)
connection.close()
C++ 客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <Winsock2.h>
#include <iostream>
#include "cJSON.h"
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
int CJSON_CDECL main(void)
{
/* print the version */
printf("Version: %s\n", cJSON_Version());
/* Now some samplecode for building objects concisely: */
//create_objects(); 创建JSON对象
cJSON* cjson_test = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* sendData = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cjson_test, "name", "mculover666");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
//cJSON_AddNumberToObject(cjson_test, "age", 22);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
cJSON_AddItemToObject(cjson_test, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("C"));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("Java"));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString("Python"));
cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cjson_test, "student");
/* 打印JSON对象(整条链表)的所有数据 */
sendData = cJSON_Print(cjson_test);
printf("发送的数据");
printf("%s\n", sendData);
/*获取JSON对象的元素*/
//释放内存
cJSON_Delete(cjson_test);
//建立链接 ,发送数据
//尝试进行socket通信 ,C++作为客户端向Python 服务端发送信息
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(1, 1);
int err;
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
return -1;
}
if (LOBYTE(wsaData.wVersion) != 1 ||
HIBYTE(wsaData.wVersion) != 1) {
WSACleanup();
return -1;
}
// 设置socket
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
if (sockClient == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //地址
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(8888); //端口
//建立链接
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//sockClient:客户端的套接字(表明即将发起连接请求),
//第二个参数是服务端的套接字所在的“地方”(“地方”是我自定义的专有名词),
//第三个参数是该“地方”的大小。
//如果请求连接成功,则返回0,否则返回错误码。
//发送
send(sockClient, sendData, strlen(sendData) + 1, 0);
//sockClient:用来传输数据的socket描述符;
//msg:一个指向要发送数据的指针;
//Len是以字节为单位的数据的长度;
//flags一般情况下置为0
//成功返回写入的字节数;执行失败,返回SOCKET_ERROR
//接收
string str = "";
char* receiveData = NULL;
int ret = 0;
char recvBuf[500]; //缓存区 ,用于存放python发送的数据
ret = recv(sockClient, recvBuf, 500, 0); //接受数据
//sockClient:接受数据的socket描述符;recvBuf 是存放接收数据的缓冲区 ; len是缓冲的长度;Flags也被置为0
// 返回值为消息长度
if (ret > 0) {
printf("接受到的数据");
printf("%s\n", recvBuf);
recvBuf[ret] = 0x00;
//解析json格式的数据
cJSON* cjson_receive = cJSON_Parse(recvBuf);
//打印JSON对象(整条链表)的所有数据
receiveData = cJSON_Print(cjson_receive);
printf("转换后的数据");
printf("%s\n", receiveData);
//获取JSON对象里的元素
cJSON * fStickLat = cJSON_GetObjectItem(cjson_receive, "fStickLat");
double m_fStickLat = cJSON_GetNumberValue(fStickLat);
printf("%.10f\n", m_fStickLat);
printf("m_fStickLat: %.10f\n", cJSON_GetNumberValue(fStickLat));
}
//关闭socket链接
closesocket(sockClient);
WSACleanup();
return 0;
}
本文来自博客园,作者:{珇逖},转载请注明原文链接:https://www.cnblogs.com/zuti666/p/16840009.html