Technical Overview
Backend design
PhoenixZMQ provides a wrapping of the C++ ZMQ library, tailored for integration within the Phoenix ecosystem. This ZMQ backend follows the double-backend architecture defined by PhoenixSocket. The PhoenixZMQ backend is structured as follows:
-
PZmqParam: This structure encapsulates all the parameters required to initialize a ZMQ socket. It includes the socket type, buffer configuration, thread affinity for reconnection handling, and data rate control. These parameters allow fine-tuning of socket behavior to match specific application requirements. -
PZmqSocket: This class defines the interface and core functions for ZMQ socket operations within the Phoenix framework. It provides methods for sending and receiving messages, as well as mechanisms for error handling during communication. The class also specifies the types used for messages and socket parameters, ensuring compatibility with the PhoenixSocket API. -
PZmqBackend: This class acts as the bridge between the ZMQ socket implementation and the Phoenix serialization layer (such asDataStream). It defines the socket, message, and parameter types used by the backend, enabling seamless integration with higher-level Phoenix components and facilitating the use of mock libraries for testing.
Note:
PhoenixZMQaims to be used with theGenericSocketdefined byPhoenixSocket.
///@brief Set of parameters to be passed to create a socket with zmq backend
struct PZmqParam{
///Socket type
int type;
///Number of messages in the buffer
int nbBufferMessage;
///Size of the message buffer in bytes
int bufferSizeByte;
///Mask of threads which deal with reconnection
size_t threadAffinity;
///Data rate
ssize_t dataRate;
};
///@brief PhoenixSocket API bridge to ZMQ socket
class PZmqSocket{
public:
///Define the type of message used by the PAbstractSocketManager
typedef zmq::message_t Message;
///Define the type of extra parameters which can be used to create a Socket used by the PAbstractSocketManager
typedef PZmqParam Param;
};
///@brief Backend to use Mock library with PAbtractSocket on ZMQ
class PZmqBackend{
public:
///Define the socket of the backend used by the PAbstractSocketManager
typedef PZmqSocket Socket;
///Define the type of message used by the PAbstractSocketManager
typedef zmq::message_t Message;
///Define the type of extra parameters which can be used to create a Socket used by the PAbstractSocketManager
typedef PZmqParam Param;
};
Component Details
- PZmqParam:
type: Specifies the ZMQ socket type (e.g., ZMQ_PUSH, ZMQ_PULL, ZMQ_REQ, ZMQ_REP).nbBufferMessage: Sets the maximum number of messages that can be buffered.bufferSizeByte: Defines the total buffer size in bytes for message storage.threadAffinity: Determines which threads are responsible for handling reconnections, improving performance and reliability in multi-threaded environments.-
dataRate: Allows control over the data transmission rate, which can be useful for throttling or bandwidth management. -
PZmqSocket:
- Implements the core socket operations, such as
sendandreceive. - Handles communication errors and provides mechanisms for error reporting and recovery.
-
Ensures compatibility with the PhoenixSocket API by defining the expected message and parameter types.
-
PZmqBackend:
- Integrates the ZMQ socket with the Phoenix serialization and deserialization mechanisms.
- Facilitates the use of mock backends for testing purposes, allowing developers to simulate network communication without requiring a live ZMQ environment.
- Defines the types used throughout the backend, ensuring consistency and type safety.
Note: In some backends, the message type is often a vector of bytes (e.g.,
std::vector<uint8_t>) wrapped in a type likeDataStreamMsg.
Functionalities
PhoenixZMQ offers the following key functionalities:
- Transparent integration of ZMQ sockets within the Phoenix ecosystem.
- Flexible socket configuration through
PZmqParam. - Robust message sending and receiving with error handling.
- Support for serialization and deserialization of messages using Phoenix's data stream utilities.
- Compatibility with mock libraries for unit testing and simulation.
How to use
To use PhoenixZMQ in your project, you can follow these steps.
-
Configure the socket parameters using
PZmqParamto match your application's requirements. -
Instantiate a socket manager using
PGenericSocketManager, specifying the data type, backend, and mock backend. For example:cpp typedef PGenericSocketManager<std::string, PZmqBackend, PMockBackend> SocketManager; SocketManager manager(mode); // mode can be PSocketMode::MOCK or PSocketMode::NO_MOCK -
Add server and client sockets to the manager:
cpp manager.addServerSocket("Alice", PSocketParam{hostName, port}, Backend::server(), "./", Mock::server()); manager.addClientSocket("Bob", PSocketParam{hostName, port}, Backend::client(), "./", Mock::client()); -
Send and receive messages using the manager's methods. For example, to send messages:
cpp for(size_t i = 0; i < nbMessage; ++i){ manager.sendData("Alice", i); }And to receive messages:cpp size_t value = 0; for(size_t i = 0; i < nbMessage; ++i){ manager.recvData("Bob", value); } -
Use mock mode for testing:
You can run your code in mock mode (PSocketMode::MOCK) to generate and test message exchanges without a real ZMQ server. This is useful for unit testing and development. -
Use real mode for actual communication:
Switch to real mode (PSocketMode::NO_MOCK) to use actual ZMQ sockets for network communication.
Example
Below is a simplified example combining the above steps:
#include "PGenericSocketManager.h"
#include "PZmqBackend.h"
typedef PGenericSocketManager<std::string, PZmqBackend, PMockBackend> SocketManager;
void example(){
size_t nbMessage = 10, port = 3390;
std::string hostName = "localhost";
PSocketMode::PSocketMode mode = PSocketMode::NO_MOCK; // or PSocketMode::MOCK for testing
SocketManager manager(mode);
manager.addServerSocket("Alice", PSocketParam{hostName, port}, SocketManager::Backend::server(), "./", SocketManager::Mock::server());
manager.addClientSocket("Bob", PSocketParam{hostName, port}, SocketManager::Backend::client(), "./", SocketManager::Mock::client());
// Sending messages
for(size_t i = 0; i < nbMessage; ++i){
manager.sendData("Alice", i);
}
// Receiving messages
size_t value = 0;
for(size_t i = 0; i < nbMessage; ++i){
manager.recvData("Bob", value);
}
}