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格式

具体实现

流程图示

image-20221029204024686

涉及到的技术

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 服务端

4b93e1bf473acf7c8ff05a600c994e38.png

e8a10adea342bd0676cfaa0abe2894ed.png

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文件放到项目中即可

image-20221029220052405

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++ 客户端

image-20221029220107420

#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;
}

posted @ 2022-10-29 22:06  英飞  阅读(2458)  评论(1编辑  收藏  举报