Skip to content

[ZeroMQ] Interfacing C++ and Python – 2 Useful Examples

In this post, I aim to write about how to use the ZeroMQ library. If you want to interface a machine learning program developed in Python, like PyTorch or TensorFlow, with another program developed in C++, inter-process communication is essential. This is why it’s often a necessary feature in the field of robotics.

In the world of computers, communication between different processes is as important as human relationships. Inter-Process Communication (IPC) is a crucial element for the smooth operation of complex systems. IPC refers to the methods by which different processes share data and collaborate, directly impacting the performance and stability of software. Especially in modern distributed systems and cloud-based services, the role of IPC has become even more critical.

In this context, ZeroMQ has established itself as a notable IPC solution. In this post, I will introduce how to use this communication framework, often employed in the industry, to exchange data between different processes. We will explore the basic concepts of this library, its features, and use cases. Furthermore, I will analyze and compare it with other libraries like LCM (Lightweight Communications and Marshalling) and ROS (Robot Operating System), discussing their differences and suitability in various environments.

If you are interested, I have some posts about robotics that you might find interesting.



zeromq

What is ZeroMQ


ZeroMQ is a library that enables high-performance Inter-Process Communication (IPC). It provides software developers with an easy way to implement distributed computing and message queuing systems. This library supports a wide range of programming languages and platforms and is very user-friendly.

One of its key features is its fast speed and low latency, making it suitable for high-performance applications, particularly in large-scale data processing and real-time systems. Additionally, ZeroMQ offers various message delivery patterns, with the most widely used patterns including Publish/Subscribe, Request/Reply, and Push/Pull.

Furthermore, ZeroMQ abstracts complex network operations internally, allowing developers to focus on message transmission without worrying about network protocols or socket management. This shortens development time and reduces developer workload through a more intuitive API.

Another advantage is that the library is lightweight and provides only the essential features, enabling efficient use of resources. This characteristic is particularly advantageous in embedded systems or environments with limited resources.

These features have made ZeroMQ an essential tool in modern software development, especially in areas like large-scale distributed systems, real-time data processing, and high-performance computing. Its simple configuration and powerful capabilities make it an ideal choice for meeting the efficient messaging requirements of various projects and applications.

Pros and Cons of ZeroMQ

ZeroMQ is a high-performance messaging library that facilitates easy implementation of Inter-Process Communication (IPC) and network communication. It provides a socket-style API, allowing communication using various messaging patterns.

  1. Message Patterns: One of the strong features of this library is its support for various message patterns, which include:
    • Publish/Subscribe (Pub/Sub): This pattern is used for efficiently delivering data to multiple receivers. The publisher sends out messages, and the subscribers selectively receive only the messages they are interested in.
    • Request/Reply (Req/Rep): This pattern is used for client-server communication. The client sends a request, and the server provides a reply.
    • Push/Pull: This pattern is used for workload distribution. The producer (push) creates tasks, and the consumers (pull) receive and process them.
  2. Asynchronous I/O Model: The library uses an asynchronous I/O model to offer high throughput and low latency. Through this model, messages are stored in an internal queue and transmitted quickly, regardless of network status or traffic.
  3. Scalability and Flexibility: The library is lightweight and has a scalable architecture. This allows it to be used in various environments, from small applications to large-scale distributed systems.
  4. Language and Platform Independence: The library can be implemented in various programming languages like C++, Python, .NET, Java, and is operable on different operating systems. This means it can be applied in a wide range of development environments.
  5. Socket Level Options: The library provides various socket options, allowing control over message size, queue length, timeouts, etc. This enables fine-tuned communication control.
  6. Security: While the library does not inherently provide encryption or authentication, there is scope for additional implementation. Typically, the library can be configured to operate on top of existing security layers (e.g., SSL/TLS).

Example Codes


Now, I will introduce an example of communication between Python and C++ programs using ZeroMQ. The following example is a simple illustration focusing solely on message transmission, but it can be adapted and applied to other Python or C++ programs as needed.

Installing ZeroMQ C++ Package

Windows


For installation on Windows, it is recommended to use vcpkg. If you have never used vcpkg before, you can refer to the link below for initial setup. Once the setup is complete, you can proceed to use it. Here are the basic commands for installation:

#Clone vcpkg from github
git clone https://github.com/microsoft/vcpkg

#locate vcpkg folder
cd vcpkg

#install vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install


If you have vcpkg ready, you can install as follows. If your environment is not a 64-bit CPU environment, you only need to write up to ‘zeromq’.

.\vcpkg install zeromq:x64-windows

Ubuntu

For Ubuntu, the package can be installed using the command below:

sudo apt-get install libzmq3-dev

Install Python Package

Due to Python’s cross-platform nature, you can install the package using the following command regardless of the operating system.

pip install pyzmq

ZeroMQ C++ Server Example Code


First, let’s create a C++ program that will act as the server. In this example, we will use CMake for building the program.

# CMakeList.txt : CMake project for zmq_cpp_server_ex, include source and define
# kongineer.com
#

cmake_minimum_required(VERSION 3.8)
project(zeromq_cpp_server_ex)

# Find ZeroMQ
find_package(ZeroMQ REQUIRED)

# Add your source files
add_executable(${PROJECT_NAME} main.cpp)

# Include directories
target_include_directories(${PROJECT_NAME} PRIVATE ${ZeroMQ_INCLUDE_DIR})

# Link the Library
target_link_libraries(${PROJECT_NAME} PRIVATE ${ZeroMQ_LIBRARIES})


If you encounter a problem where CMake cannot find ZeroMQ, you can specify the path directly. This can be done by adjusting the CMake configuration to point to the location where ZeroMQ is installed.

CMake Error at CMakeLists.txt:12 (find_package):
  
  ...

  Could not find a package configuration file provided by "ZeroMQ" with any
  of the following names:

    ZeroMQConfig.cmake
    zeromq-config.cmake

“Create a ‘cmake’ folder in your project directory, and then create a ‘FindZeroMQ.cmake’ file within it. Copy and paste the code from the provided link into this file and save it. Afterwards, add the following code to your ‘CMakeLists.txt’ file.”

# Add this if cmake fails to find the package
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

Next, let’s create the main cpp file. If it already exists automatically, you can erase everything and rewrite it. The original version, before my modifications, can be viewed at the provided location.

// Original Code from http://kr.zeromq.org/cpp:hwserver
// kongineer.com


#include <zmq.hpp>
#include <string>
#include <iostream>

#ifdef _WIN32
#include<Windows.h>
#elif defined __unix__
#include <unistd.h>
#endif

void delay(int msec)
{
#ifdef _WIN32
    Sleep(msec);
#elif defined __unix__
    sleep(msec);
#endif
}

int main() {
    // Initialize the context
    zmq::context_t context(1);

    // Create a socket of type REP (reply)
    zmq::socket_t socket(context, ZMQ_REP);

    // Bind the socket to a TCP address
    std::cout << "Starting the server on port 5555..." << std::endl;
    socket.bind("tcp://*:5555");

    while (true) {
        zmq::message_t request;

        // Wait for the next request from a client
        socket.recv(&request);
        std::string received_message(static_cast<char*>(request.data()), request.size());
        std::cout << "Received: " << received_message << std::endl;

        // Simulate some work
        delay(1);

        // Send a reply back to the client
        std::string reply_message = "Hi from ZeroMQ C++ Server";
        zmq::message_t reply(reply_message.size());
        memcpy(reply.data(), reply_message.data(), reply_message.size());
        socket.send(reply, zmq::send_flags::none);
    }

    return 0;
}

ZeroMQ Python Client Example Code


Now, let’s create a Python program that will act as the client. I’ve slightly modified the base code shared here.

"""
Original code from https://zeromq.org/languages/python/
"""

import zmq

context = zmq.Context()

#  Connect to the server
print("Connecting to cpp server…")
socket = context.socket(zmq.REQ)
socket.connect("tcp://localhost:5555")

#Send data
message_out_str = "Hi from Python ZeroMQ Client"
message_out_bytes = message_out_str.encode("utf-8")

print("Sending request …")
socket.send(message_out_bytes)

#Get the server response
message_in_bytes = socket.recv()
# Convert message bytes to string
message_in_str = message_in_bytes.decode("utf-8")
print("Received [ %s ]" % message_in_str)

Run the example codes

Let’s run the two programs mentioned above to observe the communication process. First, run the C++ server and then execute the Python client. You will be able to see the communication results as follows.

zmq

Using this library, you can exchange data in the same way between programs that are not only written in different languages but also located on different operating systems.

In the above example, we only exchanged strings, but it’s also possible to send and receive larger files, such as OpenCV mat, without much difficulty. I will cover this in more detail in a separate post.

Conclusion


In this post, we explored the features of ZeroMQ, LCM, and ROS, and provided examples of inter-process communication. Each of these offers a unique approach to fulfilling the needs of inter-process communication. ZeroMQ focuses on versatility and performance, LCM shows strength in real-time systems and robotics for data marshalling and communication, while ROS provides a comprehensive solution in the field of robotics.

The success of a project begins with the selection of the right tools. Depending on the project’s requirements, performance criteria, development environment, and objectives, it’s crucial to choose the most suitable communication solution. Whether it’s any of the libraries introduced, each tool can provide an optimized solution for specific needs and environments. If the requirements are not overly demanding, any of these tools should suffice without significant issues.

Leave a Reply

Your email address will not be published. Required fields are marked *