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()

  • c++读取管道,创建node2
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstring>
#include <sstream>

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

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()

  • 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()

  • c++ 客户端

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

#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
#include <iostream>


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

  • c++ 服务端
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

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

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) 
  • 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)

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

4. ros/ros2通信

5. 工程代码

repo

simple_python_cpp_communication

cpp编译

cd cpp/test/build
cmake .. && make -j4

管道通信

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

socket通信

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

共享内存

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

参考

  • 18
    点赞
  • 108
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
PythonC++可以通过共享内存进行通信,其中C++可以使用操作系统提供的共享内存API,而Python可以使用multiprocessing库中的Value和Array对象来实现共享内存。具体实现步骤如下: 1. C++中使用共享内存API,可以使用以下函数: - shmget():创建或打开一个共享内存区域。 - shmat():将共享内存区域映射到进程的地址空间。 - shmdt():解除共享内存区域的映射。 - shmctl():控制共享内存区域的属性。 2. Python中使用multiprocessing库中的Value和Array对象,可以使用以下代码实现共享内存: - Value:创建一个共享变量。 - Array:创建一个共享数组。 以下是一个PythonC++共享内存的示例代码: C++代码: ```c++ #include <sys/ipc.h> #include <sys/shm.h> #include <iostream> using namespace std; int main() { key_t key = ftok(".", 'a'); // 创建共享内存的key int shmid = shmget(key, 1024, IPC_CREAT | 0666); // 创建共享内存 char* shmaddr = (char*)shmat(shmid, NULL, 0); // 映射共享内存到进程地址空间 while (true) { if (*shmaddr != '\0') // 判断共享内存中是否有数据 { cout << "C++ received message: " << shmaddr << endl; // 打印收到的数据 *shmaddr = '\0'; // 清空共享内存 } } shmdt(shmaddr); // 解除映射 shmctl(shmid, IPC_RMID, NULL); // 删除共享内存 return 0; } ``` Python代码: ```python import multiprocessing as mp import time def send_message(shm): while True: message = input("Enter message to send to C++: ") shm.value = message.encode('utf-8') # 将数据写入共享内存 def main(): key = mp.Value('i', 100) # 共享内存的key shm = mp.Array('c', 1024) # 创建共享内存 p = mp.Process(target=send_message, args=(shm,)) # 创建一个子进程用于发送数据 p.start() while True: if shm.value != b'\x00': # 判断共享内存中是否有数据 message = shm.value.decode('utf-8').rstrip('\x00') # 读取共享内存中的数据 print("Python received message: ", message) # 打印收到的数据 shm.value = b'\x00' # 清空共享内存 time.sleep(0.1) p.join() if __name__ == '__main__': main() ``` 在上述示例代码中,Python进程通过Value和Array对象创建了一个共享内存区域,并在子进程中向共享内存中写入数据。C++进程通过共享内存API映射了共享内存区域,并在循环中读取共享内存中的数据。这样就实现了PythonC++之间的共享内存通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值