ZMQ Round trip in C++

authors:Sam Laan
date:Jun 2020

Description

The round trip C++ directory can be found in src/demonstrators/ComparedToOtherProtocols/ZMQ/RoundTrip/C++/. This directory contains a folder for the round trip implementation as well as the flood implementation.

This page contains information about the round trip and flood applications made in C++ using ZMQ.

Dependencies

The dependencies necessary for successful compilation of the round trip / flood application in C++ are the following:

  • CMake
  • C++17 compiler (GCC 7+ for example)
  • cppzmq library (installation: ZMQ C++)

Round trip

This chapter contains information about the round trip application. This application can be found in the directory: src/demonstrators/ComparedToOtherProtocols/ZMQ/RoundTrip/C++/RoundTrip.

Configuring

The CMake file can be found in the source directory of the round trip. This file can be configured to either build the implementation using polling for the round trip or the one using a blocking receive method. To build the blocking receive implementation make the following change to the Cmake file.

set(PROJECT roundtrip_read)

The line to change can be found at the top of the Cmake file. To build the polling implementation change the following line in the Cmake file to:

set(PROJECT roundtrip_polling)

Building

To build the application run the following commands in the round trip implementation source directory.

mkdir build && cd build
cmake ..
make

These commands will compile the source code and generate an executable.

Execution

The application takes several parameters for its execution. It can be executed using the following command in the build folder:

./RoundTrip <device ID> <Total devices> <Round trips> <Pub address> <Sub address>

A more thorough description of the parameters can be found when executing the application without parameters or can be read from the source of main.cpp. The following example starts 4 devices for the round trip using ZMQ. The slaves of the round trip are started in the background. Only the master is started in the foreground in this example. Each device pings a total of 1000 times. Therefore, there are 1000 round trips.

./RoundTrip 4 4 1000 tcp://127.0.0.1:5554 tcp://127.0.0.1:5553 &
./RoundTrip 3 4 1000 tcp://127.0.0.1:5553 tcp://127.0.0.1:5552 &
./RoundTrip 2 4 1000 tcp://127.0.0.1:5552 tcp://127.0.0.1:5551 &
./RoundTrip 1 4 1000 tcp://127.0.0.1:5551 tcp://127.0.0.1:5554

Note

Execution of the program

The round trip is initiated by the device with <device ID> = 1. Therefore, <device ID> = 1 should always be started last.

The devices added with <device ID> must be an increment of the previous one. This function does not dynamically search for the next device! So if there are four devices, device 2 should be started first, then 3 after that 4 and as last device 1 should be started as 1 should always be started last.

The <Pub address> parameter contains the address to which the device publishes. The <Sub address> parameter contains the address to which the device subscribes. Each device should subscribe to the subsequent device. This means that device 2 should subscribe to the publish address of device 1. Device 1 should subscribe to the publish address of the last device to complete the circle.

The <Round trips> and <Total devices> parameter should be the same for each application.

Implementation

Each round trip application creates a publish and a subscribe ZMQ socket. The devices publish on their pub socket while another device subscribes to these messages by connecting to the publish socket and subscribing to the topic. So the application with ID = 1 publishes to the application with ID = 2 and so on. The client reads from its topic. These topics have the name “roundtrip” with the ID behind it. So the topic with ID = 1 is “roundtrip1”.

The application with ID = 1 initiates the round trip. Therefore, it starts with publishing to topic “roundtrip2”. The application with ID = 2 then receives the message of the application with ID = 1 and sends a message to the next application. The last application always publishes to the first, making the round trip complete.

Read

The read implementation continuously executes the try_consume_message function to get the latest message of its topic. try_consume_message tries to read the next message from the queue without blocking. When a message is received, the application uses publish to publish to the next application.

Callback

The callback implementation uses a callback function for receiving the latest message. This callback sets a message flag which will trigger the publishing of a message to the next application.

Flood

This chapter contains information about the flood application. This application can be found in the directory: src/demonstrators/ComparedToOtherProtocols/ZMQ/RoundTrip/C++/Flood.

Building

First, go to the flood C++ directory (see Flood).

Execute the following commands:

mkdir build && cd build
cmake ..
make

These commands will compile the source code and generate an executable.

Execution

The application takes several parameters for its execution. It can be executed using the following command in the build folder:

./Flood <device ID> <Total devices> <Messages> <Pub address> <Sub address>

A more thorough description of the parameters can be found when executing the application without parameters or can be read from the source of main.cpp. The following example starts 4 devices for the flood using ZMQ. The slaves of the flood are started in the background. There are a total of 1000 messages sent by the master. For a flood to be finished, these messages should all be correctly received by the master.

./Flood 4 4 1000 tcp://127.0.0.1:5554 tcp://127.0.0.1:5553 &
./Flood 3 4 1000 tcp://127.0.0.1:5553 tcp://127.0.0.1:5552 &
./Flood 2 4 1000 tcp://127.0.0.1:5552 tcp://127.0.0.1:5551 &
./Flood 1 4 1000 tcp://127.0.0.1:5551 tcp://127.0.0.1:5554

Note

Execution of the program

The flood is initiated by the device with <device ID> = 1. Therefore, <device ID> = 1 should always be started last.

The devices added with <device ID> must be an increment of the previous one. This function does not dynamically search for the next device! So if there are four devices, device 2 should be started first, then 3, after that 4 and as last device 1 should be started as 1 should always be started last.

The <Pub address> parameter contains the address to which the device publishes. The <Sub address> parameter contains the address to which the device subscribes. Each device should subscribe to the subsequent device. This means that device 2 should subscribe to the publish address of device 1. Device 1 should subscribe to the publish address of the last device to complete the circle.

The <Messages> and <Total devices> parameter should be the same for each application.

Implementation

The flood application creates a publish and a subscribe ZMQ socket. The devices publish their pub socket while another device subscribes to these messages by connecting to the publish socket and subscribing to the topic. So the application with ID = 1 publishes to the application with ID = 2 and so on. The client reads from its topic. These topics have the name “flood” with the ID behind it. So the topic with ID = 1 is “flood1”.

The application with ID = 1 initiates the flood. It does this by publishing to topic “flood2”. The application with ID = 2 then receives the messages of the application with ID = 1 and sends a message to the next application for each message received.

The slaves (ID not equal to 1) just send a message to the next device in the flood loop as soon as they receive a message. The master (ID = 1) sends messages as fast as possible to the next device in the flood loop.

If the master sends a message, a value is incremented to keep track of the number of messages sent. The master keeps sending messages until this value is equal to the <total messages> parameter that the user inserted when executing the application. If the master receives a message, another value is incremented. In the end, this value should also be the same as the <total messages> parameter that the user inserted when executing the application.

Slave

The slave implementation uses a callback which sets a message flag on receiving a message. these messages are received on its topic. When a message is received, the application uses publish() for publishing a message to the next application.

Master

The master implementation uses a callback which sets a message flag on receiving a message. these messages are received on its topic. Afterwards, it writes a new message to the next device in the flood loop.

Therefore, in the beginning, the master only writes to the next devices. Afterwards, the loop will eventually be in a stable condition where the master reads a message first and writes a new message afterwards. In the end, the master won’t send messages anymore, and will only read the last messages that exist in the flood loop.