• Python 与 C++ 的进程通信


    Python与C++的进程通信

    因近期工作需要,需要用python解析数据发送到c++程序端做处理,然后用Python可视化c++端的结果,故汇总了一些python与c++进程间通信的方式。

    代码都是在Ubuntu上开发的,在windows上可能不通用。都是些简单的示例,可以在这个基础上扩展。都是些简单代码,复杂的实现就不讨论了…

    同时,这些通信方式在同语言之间的进程也是可以通用的。另外,工程文件源码在文章末尾,记得点赞收藏。

    1. 管道通信

    最简单的做法,本地新建两个文件,相互读写,但需要顺序性,所以用到管道的阻塞特性。

    • 可以先参考Linux管道读写阻塞,大致是管道可以理解为文件,写的时候读会阻塞,读的时候写会阻塞。

    • 定义python程序为node1,c++程序为node2。node1往node2发送信息为管道1,node2往node1发送信息为管道2。

    • 执行的顺序如下,node1写入管道1,node2读取管道1,node2写入管道2,node1读取管道1。因为管道的阻塞性,因此可以实现该顺序的通信。

    • 示例如下,放了跨语言通信的demo,另外c++和python单语言进程间通信的代码也写了,见文章末尾。

    fifo

    • python创建管道,以及node1。数据是可以用json传输的,方便两语言之间的数据读取,这里的简单示例就只用了简单的字符串。
    import os
    import time
    
    
    # node1
    def TestIFIO():
        input_file = "/tmp/node2_to_node1.tmp"
        output_file = "/tmp/node1_to_node2.tmp"
    
        # 1.create fifo
        if not os.path.exists(input_file):
            os.mkfifo(input_file, 0o666)
        if not os.path.exists(output_file):
            os.mkfifo(output_file, 0o666)
    
        # 2.open pipe
        print('init write pipe: ' + output_file)
        fout = os.open(output_file, os.O_WRONLY)
        print('init read pipe:  ' + input_file)
        fin = os.open(input_file, os.O_RDONLY)
    
        # 3.write and read data
        send_str = "How are you?"
        while True:
            try:
                os.write(fout, str.encode(send_str))
            except:
                print("node2 closed, exit!")
                break
            print("send: ", send_str)
            
            recv_str = os.read(fin, 1024).decode()[:-1]
            print("recv: ", recv_str)
            time.sleep(0.5)
    
        os.close(fin)
        os.close(fout)
    
    
    if __name__ == '__main__':
        TestIFIO()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • c++读取管道,创建node2
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    #define INPUT_PATH_NAME "/tmp/node1_to_node2.tmp"
    #define OUTPUT_PATH_NAME "/tmp/node2_to_node1.tmp"
    
    // node2
    int main() {
      int fin = open(INPUT_PATH_NAME, O_RDONLY);
      int fout = open(OUTPUT_PATH_NAME, O_WRONLY);
    
      char buffer[1024]; // 定义接受的文件大小,可以设大点,1024*1024*n
      while (true) {
        memset(buffer, 0, sizeof(buffer));
        if (read(fin, buffer, sizeof(buffer)) <= 0) {
          printf("node1 closed, exit!\n");
          break;
        }
        printf("receive: %s\n", buffer);
    
        std::stringstream ss;
        ss << "I'm fine, thx!";
        if (write(fout, ss.str().c_str(), ss.str().length() + 1) <= 0) {
          break;
        }
        printf("send   : %s\n", ss.str().c_str());
      }
      close(fin);
      close(fout);
      return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    2. Socket通信(推荐)

    个人觉得还是这个好用方便,主要跟文件操作打交道的我都不喜欢

    socket用于网络服务,把复杂的TCP/IP协议封装,具体实现几次握手不用了解了(想了解可以百度了,这里不多做介绍),只需要调用接口就可以实现数据通信了.

    socket分为服务端客户端,先启动服务端后,客户端向服务端发送数据,服务端接受数据并返回消息给客户端。

    示例使用安全可靠的TCP协议SOCK_STREAM

    socket比较方便,所以c++和python我分别都写了客户端和服务端,都比较简单,可以交叉通信,也可以同语言通信,所以总共可以四种组合通信,满足服务端和客户端的组合即可。

    socket

    • python客户端
    import socket
    import time
    
    
    def TestClient():
        server_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_client.connect(("localhost", 8888))
    
        info = "How are you?"
        while True:
            server_client.send(bytes(info, encoding='utf-8'))
            print("send   : ", info)
            recv_str = server_client.recv(1024)
            if not recv_str:
                break
            print("receive: ", recv_str.decode("utf-8"))
            time.sleep(0.5)
    
        server_client.close()
        print("server end, exit!")
        exit()
    
    
    if __name__ == '__main__':
        TestClient()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • python服务端
    import socket
    import time
    
    
    def TestServer():
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server.bind(("localhost", 8888))
        server.listen(5)
        connection, address = server.accept()
        print(connection, address)
        while True:
            recv_str = connection.recv(1024)
            recv_str = recv_str.decode("ascii")
            if not recv_str:
                break
            print("receive:{}".format(recv_str))
    
            send_str = "I'm fine, thx!"
            connection.send(bytes(send_str, encoding="ascii"))
            print("send:   {}".format(send_str))
            time.sleep(0.5)
    
        connection.close()
        server.close()
        print("client end, exit!")
        exit()
    
    
    if __name__ == '__main__':
        TestServer()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • c++ 客户端

      其中每个函数都会有返回值,示例简单就没做异常判断了,可以点开每个函数定义查看。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    
    int main() {
      // 1. 创建客户端,并连接到服务端
      int sock_client = socket(AF_INET, SOCK_STREAM, 0);
      sockaddr_in server_addr;
      memset(&server_addr, 0, sizeof(server_addr));
      server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
      server_addr.sin_family = AF_INET;
      server_addr.sin_port = htons(8888);
      connect(sock_client, (sockaddr*)&server_addr, sizeof(sockaddr));
    
      // 2. 发送数据,并接受服务端数据
      char* send_info = {"How are you?"};
      while(1) {
        send(sock_client, send_info, strlen(send_info) + 1, 0);
        printf("send:    %s\n", send_info);
    
        char recv_info[50];
        recv(sock_client, recv_info, sizeof(recv_info), 0);
        printf("receive: %s\n", recv_info);
      }
    
      // 3. 关闭客户端
      close(sock_client);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • c++ 服务端
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    #include 
    
    int main() {
      /**
       * 1. 创建服务端socket,并绑定相应ip和端口
       *    SOCK_STREAM对应的是TCP协议,安全可靠;SOCK_DGRAM是UDP协议,不可靠
       *    listen使得该进程可以接收socket的请求,成为一个服务端。对应的是客户端的connect。
      */
      int sock_server = socket(AF_INET, SOCK_STREAM, 0);
      sockaddr_in server_addr;
      memset(&server_addr, 0, sizeof(server_addr));
      server_addr.sin_family = AF_INET;
      server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
      server_addr.sin_port = htons(8888);
      bind(sock_server, (struct sockaddr *)&server_addr, sizeof(server_addr));
      listen(sock_server, 5);
    
      // 2. 服务端接受客户端的请求
      int socklen = sizeof(struct sockaddr_in);
      sockaddr_in client_addr;
      int sock_client = accept(sock_server, (struct sockaddr *)&client_addr,
                               (socklen_t *)&socklen);
      printf("client %s has connnected\n", inet_ntoa(client_addr.sin_addr));
    
      // 3. 同客户端连接,并接受数据
      char buffer[50];
      while (1) {
        memset(buffer, 0, sizeof(buffer));
        recv(sock_client, buffer, sizeof(buffer), 0);
        printf("receive: %s\n", buffer);
    
        strcpy(buffer, "I'm fine, thx!");
        send(sock_client, buffer, strlen(buffer), 0);
        printf("send   : %s\n", buffer);
      }
      // 4. 关闭
      close(sock_server);
      close(sock_client);
      return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    3. 共享内存通信

    共享内存的实现有些麻烦,同样也是开辟一块共有内存进行读写,速度也很快,正常读写没啥问题,但是通信的话存在同步性的问题,就是没有管道的阻塞性。

    • 会创建数据,读完了数据依旧存在。
    • 没法保证:写了只能读,读了之后才能写。所以需要自己添加同步机制:加互斥锁,或者加标志位。
    • 共享内存还是偏底层基础了,没有上面两个方便,但能在这基础上开发出很厉害的东西。

    基于mmap

    mmap(Memory-mapped file support),内存映射文件支持,详细见python标准库

    内存映射文件在Unix和Windows上是不同的,但本质都需要提供一个打开的文件来提供文件描述符以进行更新。

    • 因为没有同步机制,所以两个文件收发并不是同时的,所以并没有实现真正的通讯,只能算交互。但是代码还是放上来吧,记录下。(同步机制没实现,主要是python不咋会用-.- c++加个互斥锁就解决了)

    shell

    • python-node1
    import mmap
    import contextlib
    import time
    
    with open("node1.dat", "w") as fout:
        fout.write('\x00' * 1024)
    
    i = 0
    
    # node1
    while True:
        with open("node1.dat", "r+") as fout:
            with contextlib.closing(mmap.mmap(fout.fileno(), 1024, access=mmap.ACCESS_WRITE)) as m_write:
                m_write.seek(0)
                i += 1
                s = "node1: How are you?" + str(i)
                s.rjust(1024, '\x00')
                m_write.write(s.encode())
                m_write.flush()
                time.sleep(1) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • python-node2
    import mmap
    import contextlib
    import time
    import random
    
    i = 0
    
    # node2
    while True:
        with open('node1.dat', 'r') as fin:
            with contextlib.closing(mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ)) as m:
                s = m.read(1024)
                s = s.decode()
                print(s)
                i += 1
                print("node2: I'm fine, thx!" + str(i))
                time.sleep(1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    除了mmap可以处理共享内存,multiprocessing库同样可以处理,同时还有队列、管道等其他通讯方式,只是只找到python进程之间通讯的,没有跨语言的实现,参考一文读懂Python进程间通信的几种方式

    4. ros/ros2通信

    5. 工程代码

    repo

    simple_python_cpp_communication

    cpp编译

    cd cpp/test/build
    cmake .. && make -j4
    
    • 1
    • 2

    管道通信

    1. python-cpp
    # 终端1
    python python/fifo_node1_test.py
    # 终端2
    cpp/test/build/fifo_node2_test
    
    • 1
    • 2
    • 3
    • 4
    1. cpp-cpp
    # 终端1
    cpp/test/build/fifo_node1_test
    # 终端2
    cpp/test/build/fifo_node2_test
    
    • 1
    • 2
    • 3
    • 4
    1. python-python
    # 终端1
    python python/fifo_node1_test.py
    # 终端2
    python python/fifo_node2_test.py
    
    • 1
    • 2
    • 3
    • 4

    socket通信

    1. python-cpp
    # 终端1
    python python/socket_server.py
    # 终端2
    cpp/test/build/socket_clien_test
    
    • 1
    • 2
    • 3
    • 4
    1. cpp-cpp
    # 终端1
    cpp/test/build/socket_server_test
    # 终端2
    cpp/test/build/socket_clien_test
    
    • 1
    • 2
    • 3
    • 4
    1. python-python
    # 终端1
    python python/socket_server.py
    # 终端2
    python python/socket_client.py
    
    • 1
    • 2
    • 3
    • 4

    共享内存

    1. python-python(未同步)
    # 终端1
    python python/mmap_node1.py
    # 终端2
    python python/mmap_node2.py
    
    • 1
    • 2
    • 3
    • 4
    1. cpp-cpp(同步)
    # 终端1
    cpp/test/build/sm_server
    # 终端2
    cpp/test/build/sm_client
    
    • 1
    • 2
    • 3
    • 4

    参考

  • 相关阅读:
    搭建个人知识付费应用系统整体需求及详细设计
    1.2 switch实现两个数的四则运算
    第4周学习:MobileNetV1, V2, V3 SENet HybridSN
    螺杆支撑座是如何维持精度和稳定性的?
    AIGC|一次性了解向量数据库原理及应用,小白也能读懂的入门知识!【建议收藏】
    MyBatis动态设置要连接的数据库地址,用户名,密码
    vscode rust 环境搭建
    Jsoup抓取Https出现unable to find valid certification path to requested target
    C++内存泄漏排查以及几个工具
    MySQL-基础
  • 原文地址:https://blog.csdn.net/weixin_43152152/article/details/127764569