diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
new file mode 100644
index 000000000..e282e3891
--- /dev/null
+++ b/.github/workflows/cmake.yml
@@ -0,0 +1,38 @@
+name: CMake
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally
+ # well on Windows or Mac. You can convert this to a matrix build if you need
+ # cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Configure CMake
+ # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
+ # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ # Build your program with the given configuration
+ run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+
+ - name: Test
+ working-directory: ${{github.workspace}}/build
+ # Execute tests defined by the CMake configuration.
+ # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ run: ctest -C ${{env.BUILD_TYPE}}
+
diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml
new file mode 100644
index 000000000..ee1cb1167
--- /dev/null
+++ b/.github/workflows/greetings.yml
@@ -0,0 +1,16 @@
+name: Greetings
+
+on: [pull_request, issues]
+
+jobs:
+ greeting:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/first-interaction@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ issue-message: 'Message that will be displayed on users first issue'
+ pr-message: 'Message that will be displayed on users first pull request'
diff --git a/BUILD.md b/BUILD.md
index 903c29a64..e45a3ae6c 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -33,6 +33,22 @@ yum groupinstall "Development Tools"
yum -y install cmake
# install more required libraries
yum -y install libpcap-devel boost-devel boost-static
+
+# build and install libzmq
+wget https://github.com/zeromq/libzmq/archive/v4.3.2.zip
+unzip v4.3.2.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake ..
+sudo make -j4 install
+
+# build and install cppzmq
+wget https://github.com/zeromq/cppzmq/archive/v4.6.0.zip
+unzip v4.6.0.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake -DCPPZMQ_BUILD_TESTS=OFF ..
+sudo make -j4 install
```
2. Clone or download the project.
@@ -63,6 +79,22 @@ apt-get -y install build-essential
apt-get -y install cmake
# install more required libraries
apt-get -y install libpcap-dev libboost-all-dev
+
+# build and install libzmq
+wget https://github.com/zeromq/libzmq/archive/v4.3.2.zip
+unzip v4.3.2.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake ..
+sudo make -j4 install
+
+# build and install cppzmq
+wget https://github.com/zeromq/cppzmq/archive/v4.6.0.zip
+unzip v4.6.0.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake -DCPPZMQ_BUILD_TESTS=OFF ..
+sudo make -j4 install
```
2. Clone or download the project.
@@ -98,6 +130,22 @@ tar --bzip2 -xf boost_1_54_0.tar.bz2
cd boost_1_54_0/
./bootstrap.sh
./b2 install stage
+
+# build and install libzmq
+wget https://github.com/zeromq/libzmq/archive/v4.3.2.zip
+unzip v4.3.2.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake ..
+sudo make -j4 install
+
+# build and install cppzmq
+wget https://github.com/zeromq/cppzmq/archive/v4.6.0.zip
+unzip v4.6.0.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake -DCPPZMQ_BUILD_TESTS=OFF ..
+sudo make -j4 install
```
2. Clone or download the project.
@@ -129,15 +177,33 @@ make install
ls /usr/local/lib/libpcap*
```
-2. *Recommended*: install [brew](https://brew.sh/) for easier package management.
-
+3. *Recommended*: install [brew](https://brew.sh/) for easier package management.
```shell
# install boost
brew install boost
```
-2. Clone or download the project.
-3. Build the project.
+4. build and install libzmq and cppzmq
+```shell
+# build and install libzmq
+wget https://github.com/zeromq/libzmq/archive/v4.3.2.zip
+unzip v4.3.2.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake ..
+sudo make -j4 install
+
+# build and install cppzmq
+wget https://github.com/zeromq/cppzmq/archive/v4.6.0.zip
+unzip v4.6.0.zip
+cd /path/to/unziped/dir
+mkdir build && cd build
+cmake -DCPPZMQ_BUILD_TESTS=OFF ..
+sudo make -j4 install
+```
+5. Clone or download the project.
+
+6. Build the project.
```shell
cd /path/to/packet-agent
@@ -145,7 +211,7 @@ mkdir build && cd build
cmake .. && make
```
-4. Ensure the build is successful. The *bin* folder should contain four binary files.
+7. Ensure the build is successful. The *bin* folder should contain four binary files.
```shell
ls ../bin
diff --git a/CHANGES.md b/CHANGES.md
index a31c71cb0..72f3adce2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,6 +2,33 @@
The list of the most significant changes made in Netis Packet Agent over time.
+## Netis Packet Agent 0.5.2
+### Features
+* Supprot vlan packets
+
+## Netis Packet Agent 0.5.1
+### Features
+* Supprot packet forword by vxlan
+
+## Netis Packet Agent 0.5.0
+
+### Features
+* Support GRE direction in key.
+
+
+## Netis Packet Agent 0.3.6
+
+### Features
+* Support agent control plane for agent daemon over zeromq.
+
+
+## Netis Packet Agent 0.3.5
+
+### Features
+* Support option check for '--nofilter' option invalid usage. In version 0.3.5 or later, pktminerg will exit directly in the following cases:
+ - if you enable '--nofilter' option without specifying gre bind device(-B) in online mode.
+ - if you enable '--nofilter' option and gre bind device(-B) is same as packet captured interface(-i) in online mode.
+
## Netis Packet Agent 0.3.4
### Features
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f8d301975..b1f82b529 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,16 +6,16 @@ option(PROJECT_WITH_PROF "Enable profiling and coverage report analysis" OFF)
message("\${PROJECT_WITH_PROF}: ${PROJECT_WITH_PROF}")
# cmake cxx flags
if (${PROJECT_WITH_PROF})
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -fprofile-arcs -ftest-coverage")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -pthread -lrt -fprofile-arcs -ftest-coverage")
set(LINK_FLAGS "${LINK_FLAGS} -fprofile-arcs")
else ()
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -pthread -lrt")
endif ()
# set PKTMINERG_MAJOR_VERSION, PKTMINERG_MINOR_VERSION, etc.
set(PKTMINERG_MAJOR_VERSION "0")
-set(PKTMINERG_MINOR_VERSION "3")
-set(PKTMINERG_PATCH_VERSION "4")
+set(PKTMINERG_MINOR_VERSION "5")
+set(PKTMINERG_PATCH_VERSION "1")
set(PKTMINERG_VERSION_STRING "${PKTMINERG_MAJOR_VERSION}.${PKTMINERG_MINOR_VERSION}.${PKTMINERG_PATCH_VERSION}")
if(WIN32)
@@ -89,6 +89,8 @@ if(UNIX)
set(Boost_USE_STATIC_LIBS ON)
find_package(Boost 1.41.0 REQUIRED COMPONENTS program_options system filesystem)
+ find_package(cppzmq)
+
message("Boost_INCLUDE_DIRS: " ${Boost_INCLUDE_DIRS})
message("Boost_LIBRARY_DIRS: " ${Boost_LIBRARY_DIRS})
# include dir
@@ -201,17 +203,37 @@ set(SOURCE_FILES_GREDUMP
${SOURCE_FILES_PCAP}
)
+set(SOURCE_FILES_VXLANDUMP
+ ${PROJECT_SOURCE_DIR}/tools/vxlandump.cpp
+ ${SOURCE_FILES_PCAP}
+ )
+
set(SOURCE_FILES_PCAPCOMPARE
${PROJECT_SOURCE_DIR}/tools/pcapcompare.cpp
)
-set(SOURCE_FILES_PKTMINERG_BASE
- ${SOURCE_FILES_SYSHELP}
- ${SOURCE_FILES_PCAP}
- ${PROJECT_SOURCE_DIR}/src/socketgre.cpp
- ${PROJECT_SOURCE_DIR}/src/pcaphandler.cpp
- ${PROJECT_SOURCE_DIR}/src/statislog.cpp
- )
+if(WIN32)
+ set(SOURCE_FILES_PKTMINERG_BASE
+ ${SOURCE_FILES_SYSHELP}
+ ${SOURCE_FILES_PCAP}
+ ${PROJECT_SOURCE_DIR}/src/socketgre.cpp
+ ${PROJECT_SOURCE_DIR}/src/socketvxlan.cpp
+ ${PROJECT_SOURCE_DIR}/src/pcaphandler.cpp
+ ${PROJECT_SOURCE_DIR}/src/statislog.cpp
+ )
+else()
+ set(SOURCE_FILES_PKTMINERG_BASE
+ ${SOURCE_FILES_SYSHELP}
+ ${SOURCE_FILES_PCAP}
+ ${PROJECT_SOURCE_DIR}/src/socketgre.cpp
+ ${PROJECT_SOURCE_DIR}/src/socketvxlan.cpp
+ ${PROJECT_SOURCE_DIR}/src/socketzmq.cpp
+ ${PROJECT_SOURCE_DIR}/src/pcaphandler.cpp
+ ${PROJECT_SOURCE_DIR}/src/statislog.cpp
+ ${PROJECT_SOURCE_DIR}/src/agent_status.cpp
+ ${PROJECT_SOURCE_DIR}/src/agent_control_plane.cpp
+ )
+endif()
set(SOURCE_FILES_PKTMINERG
${SOURCE_FILES_PKTMINERG_BASE}
@@ -229,9 +251,11 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
if(UNIX)
set(PCAP_LIB pcap)
set(BOOST_LIB boost_program_options.a boost_system.a boost_filesystem.a)
+ set(ZMQ_LIB zmq)
else()
set(PCAP_LIB wpcap Packet)
- set(SOCKET_LIB ws2_32)
+ set(SOCKET_LIB zmq)
+ set(ZMQ_LIB )
endif()
# bin -- gredemo
@@ -248,6 +272,13 @@ if(WIN32)
endif()
target_link_libraries(gredump ${BOOST_LIB} ${PCAP_LIB} ${SOCKET_LIB})
+# bin --vxlandump
+add_executable(vxlandump ${SOURCE_FILES_VXLANDUMP})
+if (WIN32)
+ set_target_properties(vxlandump PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"")
+endif ()
+target_link_libraries(vxlandump ${BOOST_LIB} ${PCAP_LIB} ${SOCKET_LIB})
+
# bin -- pcapcompare
add_executable(pcapcompare ${SOURCE_FILES_PCAPCOMPARE})
target_link_libraries(pcapcompare ${BOOST_LIB} ${PCAP_LIB} ${SOCKET_LIB})
@@ -257,7 +288,7 @@ add_executable(pktminerg ${SOURCE_FILES_PKTMINERG})
if(WIN32)
set_target_properties(pktminerg PROPERTIES LINK_FLAGS "/MANIFESTUAC:\"level='requireAdministrator' uiAccess='false'\"")
endif()
-target_link_libraries(pktminerg ${BOOST_LIB} ${PCAP_LIB} ${SOCKET_LIB})
+target_link_libraries(pktminerg ${BOOST_LIB} ${PCAP_LIB} ${SOCKET_LIB} ${ZMQ_LIB})
if(UNIX AND NOT APPLE)
# test
@@ -275,7 +306,7 @@ if(UNIX AND NOT APPLE)
${TEST_DIR}/src/gtest-test-part.cc
${TEST_DIR}/src/gtest-typed-test.cc)
add_executable(unittest ${SOURCE_FILES_UNITTEST} ${SOURCE_FILES_PKTMINERG_BASE})
- target_link_libraries(unittest ${BOOST_LIB} pcap pthread)
+ target_link_libraries(unittest ${BOOST_LIB} pcap pthread ${ZMQ_LIB})
set_target_properties(unittest PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/test/bin")
endif()
@@ -317,10 +348,12 @@ else()
set(CPACK_DEB_COMPONENT_INSTALL ON)
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}_amd64)
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")
+ set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${PROJECT_SOURCE_DIR}/scripts/postinst")
else ()
set(CPACK_GENERATOR "RPM")
set(CPACK_RPM_COMPONENT_INSTALL ON)
- set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.el6.${CMAKE_SYSTEM_PROCESSOR})
+ set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.el6.${CMAKE_SYSTEM_PROCESSOR})
+ set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${PROJECT_SOURCE_DIR}/scripts/postinst_rpm.sh")
endif()
#set(CPACK_COMPONENTS_ALL pktminerg)
endif()
diff --git a/INSTALL.md b/INSTALL.md
index fa40e98bf..796b8c380 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -7,19 +7,83 @@
```shell
yum install libpcap wget
```
+Note: The default libpcap with TPACKET_V3 enabled has some performance issue. If performance is critical, you can remove libpcap and reinstall libpcap-1.8.1-6.fc27.x86_64.rpm or newer version which drops the TPACKET_V3 patch: http://rpm.pbone.net/index.php3/stat/22/idpl/50238989/com/changelog.html
2. Download and install the RPM package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
```shell
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
```
+
+## Ubuntu 18.04LTS
+
+1. Install libpcap and wget
+```bash
+sudo apt-get install libpcap-dev wget
+```
+
+2. Download and install the DEB package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
+```bash
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0_amd64.deb
+sudo dpkg -i netis-packet-agent-0.5.0_amd64.deb
+```
+
+3. If libpcap.so.1 not found when running pktminerg, create softlink for libpcap.so.1 in suitable directory.
+```bash
+whereis libpcap.so
+cd /path/to/libpcap.so
+ln -s libpcap.so.x.y.z libpcap.so.1
+```
+
+
## SUSE 12
-1. Download and install the RPM package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
+1. Install libpcap and wget
```shell
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+yum install libpcap wget
```
+
+2. Download and install the RPM package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
+
+```shell
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
+```
+
+
+## Windows 7/8/10 x64
+
+1. Download and Install [Winpcap](https://www.winpcap.org/install/bin/WinPcap_4_1_3.exe) of latest version.
+2. Download and Install [Microsoft Visual C++ Redistributable for Visual Studio 2017 x64](https://aka.ms/vs/15/release/vc_redist.x64.exe).
+3. Download and ZIP package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
+4. Extract pktminerg and other utilities from zip, and run it in cmd in Administrator Mode.
+
+Note: On Windows platform, you must use NIC's NT Device Name with format "\Device\NPF_{UUID}" as interface param. You can get it with following command:
+```
+ C:\> getmac /fo csv /v
+ "Connection Name","Network Adapter","Physical Address","Transport Name"
+ "Ethernet","Intel(R) Ethernet Connection (4) I219-V","8C-16-45-6B-53-B5","\Device\Tcpip_{4C25EA92-09DF-4FD3-A8B3-1B68E57443E2}"
+```
+Take last field(Transport Name) and replace "Tcpip_" with "NPF_" as follow, then you can get interface param of Windows.
+```
+ \Device\NPF_{4C25EA92-09DF-4FD3-A8B3-1B68E57443E2}
+```
+Use example:
+```
+ C:\> pktminerg -i \Device\NPF_{4C25EA92-09DF-4FD3-A8B3-1B68E57443E2} -r 172.24.103.201
+ C:\> gredump -i \Device\NPF_{4C25EA92-09DF-4FD3-A8B3-1B68E57443E2} -o capture.pcap
+```
+
+## CentOS6 or CentOS 7
+1. Install the libpcap at first
+```shell
+yum install libpcap
+```
+2. Install the packet agent from RPM package. Please find the latest package form [Releases Page](https://github.com/Netis/packet-agent/releases).
+```shell
+wget https://github.com/Netis/packet-agent/releases/download/v0.3.0/netis-packet-agent-0.3.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.3.0.el6.x86_64.rpm
+```
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
index d0b53b773..3aa3ba9bd 100755
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2018, Netis Technologies Co., Ltd. All rights reserved.
+Copyright (c) 2018-2021, Netis Technologies Co., Ltd. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
diff --git a/README-zh-Hans.md b/README-zh-Hans.md
index 78c46f628..d114b6c0c 100644
--- a/README-zh-Hans.md
+++ b/README-zh-Hans.md
@@ -2,9 +2,9 @@
[English](README.md) ∙ 简体中文
![packet agent's title](./img/title.jpg)
-# Netis Packet Agent 0.3.4
+# Netis Packet Agent 0.5.0
-[![Stable release](https://img.shields.io/badge/version-0.3.4-green.svg)](https://github.com/Netis/packet-agent/releases/tag/0.3.4)
+[![Stable release](https://img.shields.io/badge/version-0.5.0-green.svg)](https://github.com/Netis/packet-agent/releases/tag/0.5.0)
[![Software License](https://img.shields.io/badge/license-BSD3-green.svg)](./LICENSE.md)
@@ -35,14 +35,14 @@ yum install libpcap wget
2. 下载并安装RPM包。您可以从[这个地址](https://github.com/Netis/packet-agent/releases)获取最新版本的软件包。
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
```
#### SUSE12
1. 下载并安装RPM包。您可以从[这个地址](https://github.com/Netis/packet-agent/releases)获取最新版本的软件包。
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
```
#### Ubuntu 18.04LTS
@@ -53,8 +53,8 @@ sudo apt-get install libpcap-dev wget
2. 下载并安装DEB包。您可以从[这个地址](https://github.com/Netis/packet-agent/releases)获取最新版本的软件包。
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.3_amd64.deb
-sudo dpkg -i netis-packet-agent-0.3.3_amd64.deb
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0_amd64.deb
+sudo dpkg -i netis-packet-agent-0.5.0_amd64.deb
```
3. 如果提示libpcap.so找不到,到libpcap.so所在目录下创建libpcap.so.1软链接。
@@ -121,13 +121,14 @@ pktminerg -i eth0 -r 172.16.1.201 -M dont
## 文档/ 链接
* [安装](./INSTALL.md)和[使用](./USAGE.md)(仅英文版)。
* [构建条件及步骤](./BUILD.md)(仅英文版)。
-* [发布日志 / 路线图](./CHANGES.md)(仅英文版)。
+* [Packet-Agent 云平台性能评测](./docs/Packet-Agent性能评测.md)(仅中文版)。
+* [Packet-Agent libpcap 缓存与最大支持流量评测](./docs/Packet-Agent-libpcap缓存与最大支持流量评测.md)(仅中文版)。
## 贡献
您可以fork本项目并提交Pull Request。我们欢迎所有开源社区的同学提交自己的贡献。
## 许可证
-Copyright (c) 2018 Netis.
+Copyright (c) 2018 - 2020 Netis.
本项目库遵循下列许可证:
- [BSD-3-Clause](./LICENSE.md).
diff --git a/README.md b/README.md
index 5d2b31168..4210ae411 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,9 @@
English ∙ [简体中文](README-zh-Hans.md)
![packet agent's title](./img/title.jpg)
-# Netis Packet Agent 0.3.4
+# Netis Packet Agent 0.5.0
-[![Stable release](https://img.shields.io/badge/version-0.3.4-green.svg)](https://github.com/Netis/packet-agent/releases/tag/0.3.4)
+[![Stable release](https://img.shields.io/badge/version-0.5.0-green.svg)](https://github.com/Netis/packet-agent/releases/tag/0.5.0)
[![Software License](https://img.shields.io/badge/license-BSD3-green.svg)](./LICENSE.md)
## What is Netis Packet Agent?
@@ -30,20 +30,20 @@ With 3 utilities:
#### CentOS 6/7 and RedHat 7
1. Install libpcap and wget
```bash
-yum install libpcap wget
+yum install libpcap wget zeromq
```
2. Download and install the RPM package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
```
#### SUSE 12
1. Download and install the RPM package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.4.el6.x86_64.rpm
-rpm -ivh netis-packet-agent-0.3.4.el6.x86_64.rpm
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0.el6.x86_64.rpm
+rpm -ivh netis-packet-agent-0.5.0.el6.x86_64.rpm
```
@@ -55,8 +55,8 @@ sudo apt-get install libpcap-dev wget
2. Download and install the DEB package. Find the latest package from [Releases Page](https://github.com/Netis/packet-agent/releases).
```bash
-wget https://github.com/Netis/packet-agent/releases/download/v0.3.3/netis-packet-agent-0.3.3_amd64.deb
-sudo dpkg -i netis-packet-agent-0.3.3_amd64.deb
+wget https://github.com/Netis/packet-agent/releases/download/v0.5.0/netis-packet-agent-0.5.0_amd64.deb
+sudo dpkg -i netis-packet-agent-0.5.0_amd64.deb
```
3. If libpcap.so.1 not found when running pktminerg, create softlink for libpcap.so.1 in suitable directory.
@@ -92,6 +92,8 @@ Use example:
```
+## Engineering team contacts
+* [E-mail us.](mailto:developer@netis.com)
### Usage
@@ -134,7 +136,7 @@ For build precondition and steps, please refer to this [document](./BUILD.md).
Fork the project and send pull requests. We welcome pull requests from members of all open source community.
## License
-Copyright (c) 2018 Netis.
+Copyright (c) 2018 - 2020 Netis.
The content of this repository bound by the following licenses:
- The computer software is licensed under the [BSD-3-Clause](./LICENSE.md).
diff --git a/USAGE.md b/USAGE.md
index 1f88a157f..b62378538 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -19,6 +19,11 @@ Allowed options:
for test
-r [ --remoteip ] IPs set gre remote IPs, seperate by ',' Example:
-r 8.8.4.4,8.8.8.8
+ -z [ --zmq_port ] ZMQ_PORT (=0) set remote zeromq server port to receive
+ packets reliably; ZMQ_PORT default value 0
+ means disable.
+ -m [ --zmq_hwm ] ZMQ_HWM (=100) set zeromq queue high watermark; ZMQ_HWM
+ default value 100.
-k [ --keybit ] BIT (=1) set gre key bit; BIT defaults 1
-s [ --snaplen ] LENGTH (=2048) set snoop packet snaplen; LENGTH defaults
2048 and units byte
@@ -32,10 +37,12 @@ Allowed options:
--cpu ID set cpu affinity ID (Not supported on Windows platform)
--expression FILTER filter packets with FILTER; FILTER as same as
tcpdump BPF expression syntax
+ --control CONTROL_PORT set zmq listen port for agent daemon control. Control server won't
+ be up if this option is not set.(Not supported on Windows platform).
--dump specify dump file, mostly for integrated test
- --nofilter force no filter; only use when you confirm
- that the snoop interface is different from
- the gre interface
+ --nofilter force no filter; In online mode, only use when GRE interface
+ is set via CLI, AND you confirm that the snoop interface is
+ different from the gre interface.
```
@@ -57,7 +64,13 @@ Send GRE packets from this binded device. Sending will be failed when this devic
* remoteip, keybit
Parameters of GRE channel:
remoteip:GRE channel remote IP addresss (required)
-keybit:GRE protocol keybit parameter to distinguish the channel to remote IP
+keybit:GRE protocol keybit parameter to distinguish the channel to remote IP, high 4 bit is reserved for direction.
+
+
+* zmq_port, zmq_hwm
+Parameters of zeromq:
+zmq_port: set remote zeromq server port to receive packets reliably; ZMQ_PORT default value 0 means disable.
+zmq_hwm: set zeromq queue high watermark; ZMQ_HWM default value 100.
* cpu, priority
@@ -79,6 +92,54 @@ expression: This parameter is used to match and filter the packets (syntax is sa
This parameter will be invalid if "nofilter" parameter is set.
+* control
+control: set zmq listen port for agent daemon control.
+From version 0.3.6, packet-agent's control plane support daemon service via zmq(REQ/RSP), such as packet-agent status query, and packet-agent run as zmq server.
+The exchange data format list in C Language as below :
+```
+// request and response data format, between zmq client and server
+typedef struct msg {
+ /* header */
+ uint32_t magic; // must be 0x50 0x4D 0x32 0x30 in order.
+ uint32_t msglength; // msg length, include header, in bytes.
+ uint32_t action; // list below
+ uint32_t query_id; // the query id to identify each client for req flush
+
+ /* body */
+ char body[MAX_MSG_CONTENT_LENGTH];
+} __attribute__((packed)) msg_t, * msg_ptr_t;
+
+
+// support action now
+typedef enum msg_action_req_type {
+ MSG_ACTION_REQ_INVALID = 0x0000,
+ MSG_ACTION_REQ_QUERY_STATUS = 0x0001,
+ MSG_ACTION_REQ_MAX
+} msg_act_req_type_e;
+
+// action MSG_ACTION_REQ_QUERY_STATUS's response data body.
+typedef struct msg_status {
+ uint32_t ver;
+ uint32_t start_time;
+ uint32_t last_time;
+ uint32_t total_cap_bytes;
+ uint32_t total_cap_packets;
+ uint32_t total_cap_drop_count;
+ uint32_t total_filter_drop_count;
+ uint32_t total_fwd_drop_count;
+}__attribute__((packed)) msg_status_t, * msg_status_ptr_t;
+
+```
+
+ 1. Control server won't be up if this option is not set.
+ 2. Not supported on Windows platform.
+
+
+
+
+
+
+
### Examples
* Network interface example
```
@@ -185,3 +246,18 @@ Exit after receiving count packets. Default=0, No limit if count <= 0.
gredump -i eth0 -o /path/to/gredump_output.pcap
```
+## Example
+* Network interface example
+pktminerg -i eth0 -r 172.16.1.201
+
+* Pcap file example
+pktminerg -f sample.pcap -r 172.16.1.201
+
+* Filter example
+pktminerg -i eth0 -r 172.16.1.201 --expression '172.16.1.12'
+
+* CPU affinity and high priority example
+pktminerg -i eth0 -r 172.16.1.201 --cpu 1 -p
+
+* nofilter example, the packet capture network interface must different from the GRE output interface
+pktminerg -i eth0 -r 172.16.1.201 --nofilter
diff --git a/docker/K8S_traffic_mirror.md b/docker/K8S_traffic_mirror.md
new file mode 100644
index 000000000..7ea005f92
--- /dev/null
+++ b/docker/K8S_traffic_mirror.md
@@ -0,0 +1,315 @@
+# 摘要
+ 随着应用开发和部署流程进一步跟上需求迭代速度,调度资源更灵活、方便集群化部署的容器架构逐渐推广开来。相比虚拟机为单位的云架构,封闭程度更高的容器化应用更难以实现流量采集。本文介绍了Kubernetes环境流量采集的三种方案。
+
+# 需要解决的问题
+ Kubernetes架构中,应用连同其依赖组件被打包为高度独立的单元,称为「容器」。容器化的业务应用尽可能降低了对外部环境的依赖程度,因此可以由控制器快速创建、配置和调整,灵活性非常高。但容器是一个相对完整而封闭的结构单元,无法直接在其镜像内部署流量采集组件。
+
+# 技术背景
+ 应用虽然被打包为一个个容器,但系统对其管理是通过名为Pod的结构实现的:一个Pod中可以放置单个或多个应用容器,这些同Pod的容器共享Pod内的网络、存储等资源,彼此之间可见。
+ 通过补丁,可以避免定义整个对象,只需要定义希望更改的部分。只需要定义新增的元素就可以更新一个列表。列表中已有的元素仍然保留,新增的元素和已有的元素会被合并。
+ 准入控制器是一段代码,会拦截 Kubernetes API Server 收到的请求,拦截发生在认证和鉴权完成之后,对象进行持久化之前。可以定义两种类型的 Admission webhook:Validating 和 Mutating。Validating 类型的 Webhook 可以根据自定义的准入策略决定是否拒绝请求;Mutating 类型的 Webhook 可以根据自定义配置来对请求进行编辑。
+
+# 方案一:修改 yaml 配置文件
+* 直接修改YAML文件,直接将流量采集的镜像部署到被监控应用的同一个POD中去:
+```yaml
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: nginx-deployment
+spec:
+ selector:
+ matchLabels:
+ app: nginx
+ replicas: 2
+ template:
+ metadata:
+ labels:
+ app: nginx
+ spec:
+ containers:
+ - name: nginx
+ image: nginx:1.7.9
+ ports:
+ - containerPort: 80
+ - name: monitor
+ image: docker.io/s1240zsw/centos-packet-agent:v1
+ stdin: true
+ tty: true
+```
+
+# 方案二:补丁命令
+* Patch deployment,应用部署后通过修改补丁将流量采集的镜像部署到被监控应用的同一个POD中去
+```console
+kubectl patch deployment nginx-deployment -p '{"spec":{"template":{"spec":{"containers":[{"name":"monitor","image":"docker.io/s1240zsw/centos-packet-agent:v1","stdin":true,"tty":true}]}}}}'
+```
+
+# 方案三:WebHook
+* WebHook,通过监控pod的启动停止行为,在需要监控的pod启动的时候自动将将流量采集的镜像部署到被监控应用的同一个POD中去
+ 1. 生成密钥
+ ```console
+ ./webhook-create-signed-cert.sh \
+ --service sidecar-injector-webhook-svc \
+ --secret sidecar-injector-webhook-certs \
+ --namespace default
+ ```
+ * webhook-create-signed-cert.sh
+ ```bash
+ #!/bin/bash
+ set -e
+ usage() {
+ cat <> ${tmpdir}/csr.conf
+ [req]
+ req_extensions = v3_req
+ distinguished_name = req_distinguished_name
+ [req_distinguished_name]
+ [ v3_req ]
+ basicConstraints = CA:FALSE
+ keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+ extendedKeyUsage = serverAuth
+ subjectAltName = @alt_names
+ [alt_names]
+ DNS.1 = ${service}
+ DNS.2 = ${service}.${namespace}
+ DNS.3 = ${service}.${namespace}.svc
+ EOF
+
+ openssl genrsa -out ${tmpdir}/server-key.pem 2048
+ openssl req -new -key ${tmpdir}/server-key.pem -subj "/CN=${service}.${namespace}.svc" -out ${tmpdir}/server.csr -config ${tmpdir}/csr.conf
+
+ # clean-up any previously created CSR for our service. Ignore errors if not present.
+ kubectl delete csr ${csrName} 2>/dev/null || true
+
+ # create server cert/key CSR and send to k8s API
+ cat <&2
+ exit 1
+ fi
+
+ echo ${serverCert} | openssl base64 -d -A -out ${tmpdir}/server-cert.pem
+ # create the secret with CA cert and server cert/key
+ kubectl create secret generic ${secret} \
+ --from-file=key.pem=${tmpdir}/server-key.pem \
+ --from-file=cert.pem=${tmpdir}/server-cert.pem \
+ --dry-run -o yaml |
+ kubectl -n ${namespace} apply -f -
+ ```
+ 2. 部署密钥
+ ```console
+ cat mutatingwebhook.yaml | \
+ webhook-patch-ca-bundle.sh > \
+ mutatingwebhook-ca-bundle.yaml
+ ```
+ * webhook-patch-ca-bundle.sh
+ ```bash
+ #!/bin/bash
+ ROOT=$(cd $(dirname $0)/../../; pwd)
+ set -o errexit
+ set -o nounset
+ set -o pipefail
+ export CA_BUNDLE=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}')
+
+ if command -v envsubst >/dev/null 2>&1; then
+ envsubst
+ else
+ sed -e "s|\${CA_BUNDLE}|${CA_BUNDLE}|g"
+ fi
+ ```
+ * mutatingwebhook.yaml
+ ```yaml
+ apiVersion: admissionregistration.k8s.io/v1beta1
+ kind: MutatingWebhookConfiguration
+ metadata:
+ name: sidecar-injector-webhook-cfg
+ labels:
+ app: sidecar-injector
+ webhooks:
+ - name: sidecar-injector.morven.me
+ clientConfig:
+ service:
+ name: sidecar-injector-webhook-svc
+ namespace: default
+ path: "/mutate"
+ caBundle: ${CA_BUNDLE}
+ rules:
+ - operations: [ "CREATE" ]
+ apiGroups: [""]
+ apiVersions: ["v1"]
+ resources: ["pods"]
+ namespaceSelector:
+ matchLabels:
+ sidecar-injector: enabled
+ ```
+ 3. 部署流量监控镜像
+ ```bash
+ kubectl create -f configmap.yaml
+ kubectl create -f deployment.yaml
+ kubectl create -f service.yaml
+ kubectl create -f mutatingwebhook-ca-bundle.yaml
+ ```
+ * configmap.yaml
+ ```yaml
+ apiVersion: v1
+ kind: ConfigMap
+ metadata:
+ name: sidecar-injector-webhook-configmap
+ data:
+ sidecarconfig.yaml: |
+ containers:
+ - name: sidecar-monitor
+ image: docker.io/s1240zsw/centos-packet-agent:v1
+ stdin: true
+ tty: true
+ ```
+
+ * deployment.yaml
+ ```yaml
+ apiVersion: extensions/v1beta1
+ kind: Deployment
+ metadata:
+ name: sidecar-injector-webhook-deployment
+ labels:
+ app: sidecar-injector
+ spec:
+ replicas: 1
+ template:
+ metadata:
+ labels:
+ app: sidecar-injector
+ spec:
+ containers:
+ - name: sidecar-injector
+ image: morvencao/sidecar-injector:v1
+ imagePullPolicy: IfNotPresent
+ args:
+ - -sidecarCfgFile=/etc/webhook/config/sidecarconfig.yaml
+ - -tlsCertFile=/etc/webhook/certs/cert.pem
+ - -tlsKeyFile=/etc/webhook/certs/key.pem
+ - -alsologtostderr
+ - -v=4
+ - 2>&1
+ volumeMounts:
+ - name: webhook-certs
+ mountPath: /etc/webhook/certs
+ readOnly: true
+ - name: webhook-config
+ mountPath: /etc/webhook/config
+ volumes:
+ - name: webhook-certs
+ secret:
+ secretName: sidecar-injector-webhook-certs
+ - name: webhook-config
+ configMap:
+ name: sidecar-injector-webhook-configmap
+ ```
+ * service.yaml
+ ```yaml
+ apiVersion: v1
+ kind: Service
+ metadata:
+ name: sidecar-injector-webhook-svc
+ labels:
+ app: sidecar-injector
+ spec:
+ ports:
+ - port: 443
+ targetPort: 443
+ selector:
+ app: sidecar-injector
+ ```
+ 4. 给需要监控的应用打上标签
+ ```console
+ kubectl patch deployment nginx-deployment -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar-injector-webhook.morven.me/inject": "true"}}}}}'
+ ```
+
+# 结论
+ 优先采用修改yaml文件的方法,如果没有修改yaml文件的权限,那么可以采用Patch deployment方法。如果以上方法都不能用最后再采用WebHook方法。
+
+# 参考
+1. [Istio Sidecar 注入过程解密](https://istio.io/zh/blog/2019/data-plane-setup/)
+2. [Diving into Kubernetes MutatingAdmissionWebhook](https://github.com/morvencao/kube-mutating-webhook-tutorial/blob/master/medium-article.md)
+
diff --git "a/docs/Packet-Agent-libpcap\347\274\223\345\255\230\344\270\216\346\234\200\345\244\247\346\224\257\346\214\201\346\265\201\351\207\217\350\257\204\346\265\213.md" "b/docs/Packet-Agent-libpcap\347\274\223\345\255\230\344\270\216\346\234\200\345\244\247\346\224\257\346\214\201\346\265\201\351\207\217\350\257\204\346\265\213.md"
new file mode 100644
index 000000000..1817110d0
--- /dev/null
+++ "b/docs/Packet-Agent-libpcap\347\274\223\345\255\230\344\270\216\346\234\200\345\244\247\346\224\257\346\214\201\346\265\201\351\207\217\350\257\204\346\265\213.md"
@@ -0,0 +1,22 @@
+# Packet-Agent libpcap 缓存与最大支持流量评测
+
+# 测试环境与方法
+
+pktminerg version 0.3.2 (rev: 7d844a7 build: 2019-01-21 10:27:50)
+libpcap version 1.5.3
+
+8 核 vSphere 虚拟机:Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
+内存 16GB
+
+使用 --buffsize 参数控制 pktminerg libpcap 缓存大小,使用tcpreplay往网卡发包,逐次增加发包速率,记录 pktminerg 出现丢包时的发包速率。
+
+# 测试结果
+
+
+|libpcap buffsize| 进程内存占用 | 最大支持流量 |
+|----------------|--------------|--------------|
+|18 MB | 32 MB | 549 Mbps|
+|36 MB | 50 MB | 705 Mbps|
+|64 MB | 79 MB | 862 Mbps|
+|128 MB | 145 MB | 900 Mbps|
+
diff --git "a/docs/Packet-Agent\346\200\247\350\203\275\350\257\204\346\265\213.md" "b/docs/Packet-Agent\346\200\247\350\203\275\350\257\204\346\265\213.md"
index de7e1a1ef..a0483f91e 100644
--- "a/docs/Packet-Agent\346\200\247\350\203\275\350\257\204\346\265\213.md"
+++ "b/docs/Packet-Agent\346\200\247\350\203\275\350\257\204\346\265\213.md"
@@ -13,10 +13,10 @@ libpcap version 1.5.3
| | 2 cpu | 8 cpu | 12 cpu |
|-----|-------|-------|--------|
-|通用型 centos7.6|内存占用 508.8 M
![image](../img/image2019-7-26_12-19-28.png)|内存占用 508.8 M
![image](../img/image2019-7-26_12-20-15.png)|内存占用 508.8 M
![image](../img/image2019-7-26_12-20-33.png)|
-|高主频 centos7.6|内存占用 508.8 M
![image](../img/image2019-7-26_12-21-22.png)|内存占用 508.8 M
![image](../img/image2019-7-26_12-21-51.png)|内存占用 508.8 M
![image](../img/image2019-7-26_12-22-37.png)|
-|通用型 suse12.2|内存占用:280.2 M。CPU顶峰:90.3%,1175.5 Mbps
![image](../img/image2019-7-29_11-22-40.png)|内存占用:280.2 M
![image](../img/image2019-7-26_16-52-16.png)|内存占用:280.2 M
![image](../img/image2019-7-26_16-51-39.png)|
-|高主频 suse12.2|内存占用:280.2 M。CPU顶峰:97.47%,1175.5 Mbps
![image](../img/image2019-7-29_11-23-48.png)|内存占用:280.2 M
![image](../img/image2019-7-26_16-53-32.png)|内存占用:280.2 M
![image](../img/image2019-7-26_16-55-46.png)|
+|通用型 centos7.6|内存占用 508.8 M
![image](../img/ali-1-1.png)|内存占用 508.8 M
![image](../img/ali-1-2.png)|内存占用 508.8 M
![image](../img/ali-1-3.png)|
+|高主频 centos7.6|内存占用 508.8 M
![image](../img/ali-2-1.png)|内存占用 508.8 M
![image](../img/ali-2-2.png)|内存占用 508.8 M
![image](../img/ali-2-3.png)|
+|通用型 suse12.2|内存占用:280.2 M。CPU顶峰:90.3%,1175.5 Mbps
![image](../img/ali-3-1.png)|内存占用:280.2 M
![image](../img/ali-3-2.png)|内存占用:280.2 M
![image](../img/ali-3-3.png)|
+|高主频 suse12.2|内存占用:280.2 M。CPU顶峰:97.47%,1175.5 Mbps
![image](../img/ali-4-1.png)|内存占用:280.2 M
![image](../img/ali-4-2.png)|内存占用:280.2 M
![image](../img/ali-4-3.png)|
# 阿里云机器配置
@@ -34,10 +34,10 @@ libpcap version 1.5.3
| | 2 cpu | 8 cpu | 12 cpu |
|-----|-------|-------|--------|
-|通用型 centos7.6|内存占用 508.8 M
![image](../img/image2019-8-2_10-28-14.png)|内存占用 508.8 M
![image](../img/image2019-8-2_10-29-16.png)|内存占用 508.8 M
![image](../img/image2019-8-2_10-30-17.png)|
-|高主频 centos7.6|内存占用 508.8 M
![image](../img/image2019-8-2_10-31-39.png)|内存占用 508.8 M
![image](../img/image2019-8-2_10-32-38.png)|内存占用 508.8 M
![image](../img/image2019-8-2_10-33-32.png)|
-|通用型 suse12.2|内存占用:280.2 M
![image](../img/image2019-8-1_10-22-54.png)|内存占用:280.2 M
![image](../img/image2019-8-1_10-23-45.png)|内存占用:280.2 M
![image](../img/image2019-8-1_10-24-32.png)|
-|高主频 suse12.2|内存占用:280.2 M
![image](../img/image2019-8-1_10-25-31.png)|内存占用:280.2 M
![image](../img/image2019-8-1_10-26-12.png)|内存占用:280.2 M
![image](../img/image2019-8-1_10-26-49.png)|
+|通用型 centos7.6|内存占用 508.8 M
![image](../img/aws-1-1.png)|内存占用 508.8 M
![image](../img/aws-1-2.png)|内存占用 508.8 M
![image](../img/aws-1-3.png)|
+|高主频 centos7.6|内存占用 508.8 M
![image](../img/aws-2-1.png)|内存占用 508.8 M
![image](../img/aws-2-2.png)|内存占用 508.8 M
![image](../img/aws-2-3.png)|
+|通用型 suse12.2|内存占用:280.2 M
![image](../img/aws-3-1.png)|内存占用:280.2 M
![image](../img/aws-3-2.png)|内存占用:280.2 M
![image](../img/aws-3-3.png)|
+|高主频 suse12.2|内存占用:280.2 M
![image](../img/aws-4-1.png)|内存占用:280.2 M
![image](../img/aws-4-2.png)|内存占用:280.2 M
![image](../img/aws-4-3.png)|
# aws机器配置
diff --git a/img/ali-1-1.png b/img/ali-1-1.png
new file mode 100644
index 000000000..721a27c95
Binary files /dev/null and b/img/ali-1-1.png differ
diff --git a/img/ali-1-2.png b/img/ali-1-2.png
new file mode 100644
index 000000000..4ea6be51c
Binary files /dev/null and b/img/ali-1-2.png differ
diff --git a/img/ali-1-3.png b/img/ali-1-3.png
new file mode 100644
index 000000000..616242939
Binary files /dev/null and b/img/ali-1-3.png differ
diff --git a/img/ali-2-1.png b/img/ali-2-1.png
new file mode 100644
index 000000000..547a69a19
Binary files /dev/null and b/img/ali-2-1.png differ
diff --git a/img/ali-2-2.png b/img/ali-2-2.png
new file mode 100644
index 000000000..0ecea94af
Binary files /dev/null and b/img/ali-2-2.png differ
diff --git a/img/ali-2-3.png b/img/ali-2-3.png
new file mode 100644
index 000000000..f53deda57
Binary files /dev/null and b/img/ali-2-3.png differ
diff --git a/img/ali-3-1.png b/img/ali-3-1.png
new file mode 100644
index 000000000..204b7878a
Binary files /dev/null and b/img/ali-3-1.png differ
diff --git a/img/ali-3-2.png b/img/ali-3-2.png
new file mode 100644
index 000000000..bb0780840
Binary files /dev/null and b/img/ali-3-2.png differ
diff --git a/img/ali-3-3.png b/img/ali-3-3.png
new file mode 100644
index 000000000..b2305cb57
Binary files /dev/null and b/img/ali-3-3.png differ
diff --git a/img/ali-4-1.png b/img/ali-4-1.png
new file mode 100644
index 000000000..f523039e5
Binary files /dev/null and b/img/ali-4-1.png differ
diff --git a/img/ali-4-2.png b/img/ali-4-2.png
new file mode 100644
index 000000000..81533e257
Binary files /dev/null and b/img/ali-4-2.png differ
diff --git a/img/ali-4-3.png b/img/ali-4-3.png
new file mode 100644
index 000000000..cf39f1312
Binary files /dev/null and b/img/ali-4-3.png differ
diff --git a/img/aws-1-1.png b/img/aws-1-1.png
new file mode 100644
index 000000000..e23b2cd9b
Binary files /dev/null and b/img/aws-1-1.png differ
diff --git a/img/aws-1-2.png b/img/aws-1-2.png
new file mode 100644
index 000000000..1d55774b3
Binary files /dev/null and b/img/aws-1-2.png differ
diff --git a/img/aws-1-3.png b/img/aws-1-3.png
new file mode 100644
index 000000000..e3e31c427
Binary files /dev/null and b/img/aws-1-3.png differ
diff --git a/img/aws-2-1.png b/img/aws-2-1.png
new file mode 100644
index 000000000..4cfd433f3
Binary files /dev/null and b/img/aws-2-1.png differ
diff --git a/img/aws-2-2.png b/img/aws-2-2.png
new file mode 100644
index 000000000..a5e7c9d1b
Binary files /dev/null and b/img/aws-2-2.png differ
diff --git a/img/aws-2-3.png b/img/aws-2-3.png
new file mode 100644
index 000000000..e5203038e
Binary files /dev/null and b/img/aws-2-3.png differ
diff --git a/img/aws-3-1.png b/img/aws-3-1.png
new file mode 100644
index 000000000..4e1d7a2cb
Binary files /dev/null and b/img/aws-3-1.png differ
diff --git a/img/aws-3-2.png b/img/aws-3-2.png
new file mode 100644
index 000000000..046146e43
Binary files /dev/null and b/img/aws-3-2.png differ
diff --git a/img/aws-3-3.png b/img/aws-3-3.png
new file mode 100644
index 000000000..e96da3ead
Binary files /dev/null and b/img/aws-3-3.png differ
diff --git a/img/aws-4-1.png b/img/aws-4-1.png
new file mode 100644
index 000000000..e549c9986
Binary files /dev/null and b/img/aws-4-1.png differ
diff --git a/img/aws-4-2.png b/img/aws-4-2.png
new file mode 100644
index 000000000..c588b1d36
Binary files /dev/null and b/img/aws-4-2.png differ
diff --git a/img/aws-4-3.png b/img/aws-4-3.png
new file mode 100644
index 000000000..7e8699365
Binary files /dev/null and b/img/aws-4-3.png differ
diff --git a/img/image2019-7-26_12-19-28.png b/img/image2019-7-26_12-19-28.png
deleted file mode 100644
index fa5c78492..000000000
Binary files a/img/image2019-7-26_12-19-28.png and /dev/null differ
diff --git a/img/image2019-7-26_12-20-15.png b/img/image2019-7-26_12-20-15.png
deleted file mode 100644
index dde1be3a9..000000000
Binary files a/img/image2019-7-26_12-20-15.png and /dev/null differ
diff --git a/img/image2019-7-26_12-20-33.png b/img/image2019-7-26_12-20-33.png
deleted file mode 100644
index d72a23d45..000000000
Binary files a/img/image2019-7-26_12-20-33.png and /dev/null differ
diff --git a/img/image2019-7-26_12-21-22.png b/img/image2019-7-26_12-21-22.png
deleted file mode 100644
index a382ac796..000000000
Binary files a/img/image2019-7-26_12-21-22.png and /dev/null differ
diff --git a/img/image2019-7-26_12-21-51.png b/img/image2019-7-26_12-21-51.png
deleted file mode 100644
index b73502824..000000000
Binary files a/img/image2019-7-26_12-21-51.png and /dev/null differ
diff --git a/img/image2019-7-26_12-22-37.png b/img/image2019-7-26_12-22-37.png
deleted file mode 100644
index 6fc884cd7..000000000
Binary files a/img/image2019-7-26_12-22-37.png and /dev/null differ
diff --git a/img/image2019-7-26_16-51-39.png b/img/image2019-7-26_16-51-39.png
deleted file mode 100644
index bd5ebf8ac..000000000
Binary files a/img/image2019-7-26_16-51-39.png and /dev/null differ
diff --git a/img/image2019-7-26_16-52-16.png b/img/image2019-7-26_16-52-16.png
deleted file mode 100644
index e9f372ff6..000000000
Binary files a/img/image2019-7-26_16-52-16.png and /dev/null differ
diff --git a/img/image2019-7-26_16-53-32.png b/img/image2019-7-26_16-53-32.png
deleted file mode 100644
index efffdbc9d..000000000
Binary files a/img/image2019-7-26_16-53-32.png and /dev/null differ
diff --git a/img/image2019-7-26_16-55-46.png b/img/image2019-7-26_16-55-46.png
deleted file mode 100644
index d62f2d8bb..000000000
Binary files a/img/image2019-7-26_16-55-46.png and /dev/null differ
diff --git a/img/image2019-7-29_11-22-40.png b/img/image2019-7-29_11-22-40.png
deleted file mode 100644
index 9f1c45093..000000000
Binary files a/img/image2019-7-29_11-22-40.png and /dev/null differ
diff --git a/img/image2019-7-29_11-23-48.png b/img/image2019-7-29_11-23-48.png
deleted file mode 100644
index 06e4ae892..000000000
Binary files a/img/image2019-7-29_11-23-48.png and /dev/null differ
diff --git a/img/image2019-8-1_10-22-54.png b/img/image2019-8-1_10-22-54.png
deleted file mode 100644
index cb4593b5c..000000000
Binary files a/img/image2019-8-1_10-22-54.png and /dev/null differ
diff --git a/img/image2019-8-1_10-23-45.png b/img/image2019-8-1_10-23-45.png
deleted file mode 100644
index 0ffe22baf..000000000
Binary files a/img/image2019-8-1_10-23-45.png and /dev/null differ
diff --git a/img/image2019-8-1_10-24-32.png b/img/image2019-8-1_10-24-32.png
deleted file mode 100644
index 31286c8cb..000000000
Binary files a/img/image2019-8-1_10-24-32.png and /dev/null differ
diff --git a/img/image2019-8-1_10-25-31.png b/img/image2019-8-1_10-25-31.png
deleted file mode 100644
index f56e2e300..000000000
Binary files a/img/image2019-8-1_10-25-31.png and /dev/null differ
diff --git a/img/image2019-8-1_10-26-12.png b/img/image2019-8-1_10-26-12.png
deleted file mode 100644
index bafb88d36..000000000
Binary files a/img/image2019-8-1_10-26-12.png and /dev/null differ
diff --git a/img/image2019-8-1_10-26-49.png b/img/image2019-8-1_10-26-49.png
deleted file mode 100644
index de40fcead..000000000
Binary files a/img/image2019-8-1_10-26-49.png and /dev/null differ
diff --git a/img/image2019-8-2_10-28-14.png b/img/image2019-8-2_10-28-14.png
deleted file mode 100644
index 5768e7a14..000000000
Binary files a/img/image2019-8-2_10-28-14.png and /dev/null differ
diff --git a/img/image2019-8-2_10-29-16.png b/img/image2019-8-2_10-29-16.png
deleted file mode 100644
index 472982fa4..000000000
Binary files a/img/image2019-8-2_10-29-16.png and /dev/null differ
diff --git a/img/image2019-8-2_10-30-17.png b/img/image2019-8-2_10-30-17.png
deleted file mode 100644
index 8834025f5..000000000
Binary files a/img/image2019-8-2_10-30-17.png and /dev/null differ
diff --git a/img/image2019-8-2_10-31-39.png b/img/image2019-8-2_10-31-39.png
deleted file mode 100644
index 158af3583..000000000
Binary files a/img/image2019-8-2_10-31-39.png and /dev/null differ
diff --git a/img/image2019-8-2_10-32-38.png b/img/image2019-8-2_10-32-38.png
deleted file mode 100644
index 0b75d9c8d..000000000
Binary files a/img/image2019-8-2_10-32-38.png and /dev/null differ
diff --git a/img/image2019-8-2_10-33-32.png b/img/image2019-8-2_10-33-32.png
deleted file mode 100644
index d36b83a18..000000000
Binary files a/img/image2019-8-2_10-33-32.png and /dev/null differ
diff --git a/include/zmq.hpp b/include/zmq.hpp
new file mode 100644
index 000000000..d02a208f9
--- /dev/null
+++ b/include/zmq.hpp
@@ -0,0 +1,2688 @@
+/*
+ Copyright (c) 2016-2017 ZeroMQ community
+ Copyright (c) 2009-2011 250bpm s.r.o.
+ Copyright (c) 2011 Botond Ballo
+ Copyright (c) 2007-2009 iMatix Corporation
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+*/
+
+#ifndef __ZMQ_HPP_INCLUDED__
+#define __ZMQ_HPP_INCLUDED__
+
+#ifdef _WIN32
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#endif
+
+// included here for _HAS_CXX* macros
+#include
+
+#if defined(_MSVC_LANG)
+#define CPPZMQ_LANG _MSVC_LANG
+#else
+#define CPPZMQ_LANG __cplusplus
+#endif
+// overwrite if specific language macros indicate higher version
+#if defined(_HAS_CXX14) && _HAS_CXX14 && CPPZMQ_LANG < 201402L
+#undef CPPZMQ_LANG
+#define CPPZMQ_LANG 201402L
+#endif
+#if defined(_HAS_CXX17) && _HAS_CXX17 && CPPZMQ_LANG < 201703L
+#undef CPPZMQ_LANG
+#define CPPZMQ_LANG 201703L
+#endif
+
+// macros defined if has a specific standard or greater
+#if CPPZMQ_LANG >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
+#define ZMQ_CPP11
+#endif
+#if CPPZMQ_LANG >= 201402L
+#define ZMQ_CPP14
+#endif
+#if CPPZMQ_LANG >= 201703L
+#define ZMQ_CPP17
+#endif
+
+#if defined(ZMQ_CPP14) && !defined(_MSC_VER)
+#define ZMQ_DEPRECATED(msg) [[deprecated(msg)]]
+#elif defined(_MSC_VER)
+#define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg))
+#elif defined(__GNUC__)
+#define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg)))
+#endif
+
+#if defined(ZMQ_CPP17)
+#define ZMQ_NODISCARD [[nodiscard]]
+#else
+#define ZMQ_NODISCARD
+#endif
+
+#if defined(ZMQ_CPP11)
+#define ZMQ_NOTHROW noexcept
+#define ZMQ_EXPLICIT explicit
+#define ZMQ_OVERRIDE override
+#define ZMQ_NULLPTR nullptr
+#define ZMQ_CONSTEXPR_FN constexpr
+#define ZMQ_CONSTEXPR_VAR constexpr
+#define ZMQ_CPP11_DEPRECATED(msg) ZMQ_DEPRECATED(msg)
+#else
+#define ZMQ_NOTHROW throw()
+#define ZMQ_EXPLICIT
+#define ZMQ_OVERRIDE
+#define ZMQ_NULLPTR 0
+#define ZMQ_CONSTEXPR_FN
+#define ZMQ_CONSTEXPR_VAR const
+#define ZMQ_CPP11_DEPRECATED(msg)
+#endif
+#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900)
+#define ZMQ_EXTENDED_CONSTEXPR
+#endif
+#if defined(ZMQ_CPP17)
+#define ZMQ_INLINE_VAR inline
+#else
+#define ZMQ_INLINE_VAR
+#endif
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef ZMQ_CPP11
+#include
+#include
+#include
+#include
+#endif
+
+#if defined(__has_include) && defined(ZMQ_CPP17)
+#define CPPZMQ_HAS_INCLUDE_CPP17(X) __has_include(X)
+#else
+#define CPPZMQ_HAS_INCLUDE_CPP17(X) 0
+#endif
+
+#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_OPTIONAL)
+#define CPPZMQ_HAS_OPTIONAL 1
+#endif
+#ifndef CPPZMQ_HAS_OPTIONAL
+#define CPPZMQ_HAS_OPTIONAL 0
+#elif CPPZMQ_HAS_OPTIONAL
+#include
+#endif
+
+#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_STRING_VIEW)
+#define CPPZMQ_HAS_STRING_VIEW 1
+#endif
+#ifndef CPPZMQ_HAS_STRING_VIEW
+#define CPPZMQ_HAS_STRING_VIEW 0
+#elif CPPZMQ_HAS_STRING_VIEW
+#include
+#endif
+
+/* Version macros for compile-time API version detection */
+#define CPPZMQ_VERSION_MAJOR 4
+#define CPPZMQ_VERSION_MINOR 7
+#define CPPZMQ_VERSION_PATCH 1
+
+#define CPPZMQ_VERSION \
+ ZMQ_MAKE_VERSION(CPPZMQ_VERSION_MAJOR, CPPZMQ_VERSION_MINOR, \
+ CPPZMQ_VERSION_PATCH)
+
+// Detect whether the compiler supports C++11 rvalue references.
+#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) \
+ && defined(__GXX_EXPERIMENTAL_CXX0X__))
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION = delete
+#elif defined(__clang__)
+#if __has_feature(cxx_rvalue_references)
+#define ZMQ_HAS_RVALUE_REFS
+#endif
+
+#if __has_feature(cxx_deleted_functions)
+#define ZMQ_DELETED_FUNCTION = delete
+#else
+#define ZMQ_DELETED_FUNCTION
+#endif
+#elif defined(_MSC_VER) && (_MSC_VER >= 1900)
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION = delete
+#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
+#define ZMQ_HAS_RVALUE_REFS
+#define ZMQ_DELETED_FUNCTION
+#else
+#define ZMQ_DELETED_FUNCTION
+#endif
+
+#if defined(ZMQ_CPP11) && !defined(__llvm__) && !defined(__INTEL_COMPILER) \
+ && defined(__GNUC__) && __GNUC__ < 5
+#define ZMQ_CPP11_PARTIAL
+#elif defined(__GLIBCXX__) && __GLIBCXX__ < 20160805
+//the date here is the last date of gcc 4.9.4, which
+// effectively means libstdc++ from gcc 5.5 and higher won't trigger this branch
+#define ZMQ_CPP11_PARTIAL
+#endif
+
+#ifdef ZMQ_CPP11
+#ifdef ZMQ_CPP11_PARTIAL
+#define ZMQ_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
+#else
+#include
+#define ZMQ_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value
+#endif
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0)
+#define ZMQ_NEW_MONITOR_EVENT_LAYOUT
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
+#define ZMQ_HAS_PROXY_STEERABLE
+/* Socket event data */
+typedef struct
+{
+ uint16_t event; // id of the event as bitfield
+ int32_t value; // value is either error code, fd or reconnect interval
+} zmq_event_t;
+#endif
+
+// Avoid using deprecated message receive function when possible
+#if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0)
+#define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags)
+#endif
+
+
+// In order to prevent unused variable warnings when building in non-debug
+// mode use this macro to make assertions.
+#ifndef NDEBUG
+#define ZMQ_ASSERT(expression) assert(expression)
+#else
+#define ZMQ_ASSERT(expression) (void) (expression)
+#endif
+
+namespace zmq
+{
+#ifdef ZMQ_CPP11
+namespace detail
+{
+namespace ranges
+{
+using std::begin;
+using std::end;
+template auto begin(T &&r) -> decltype(begin(std::forward(r)))
+{
+ return begin(std::forward(r));
+}
+template auto end(T &&r) -> decltype(end(std::forward(r)))
+{
+ return end(std::forward(r));
+}
+} // namespace ranges
+
+template using void_t = void;
+
+template
+using iter_value_t = typename std::iterator_traits::value_type;
+
+template
+using range_iter_t = decltype(
+ ranges::begin(std::declval::type &>()));
+
+template using range_value_t = iter_value_t>;
+
+template struct is_range : std::false_type
+{
+};
+
+template
+struct is_range<
+ T,
+ void_t::type &>())
+ == ranges::end(std::declval::type &>()))>>
+ : std::true_type
+{
+};
+
+} // namespace detail
+#endif
+
+typedef zmq_free_fn free_fn;
+typedef zmq_pollitem_t pollitem_t;
+
+class error_t : public std::exception
+{
+ public:
+ error_t() ZMQ_NOTHROW : errnum(zmq_errno()) {}
+ explicit error_t(int err) ZMQ_NOTHROW : errnum(err) {}
+ virtual const char *what() const ZMQ_NOTHROW ZMQ_OVERRIDE
+ {
+ return zmq_strerror(errnum);
+ }
+ int num() const ZMQ_NOTHROW { return errnum; }
+
+ private:
+ int errnum;
+};
+
+inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1)
+{
+ int rc = zmq_poll(items_, static_cast(nitems_), timeout_);
+ if (rc < 0)
+ throw error_t();
+ return rc;
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(zmq_pollitem_t const *items_, size_t nitems_, long timeout_ = -1)
+{
+ return poll(const_cast(items_), nitems_, timeout_);
+}
+
+#ifdef ZMQ_CPP11
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int
+poll(zmq_pollitem_t const *items, size_t nitems, std::chrono::milliseconds timeout)
+{
+ return poll(const_cast(items), nitems,
+ static_cast(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(std::vector const &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(const_cast(items.data()), items.size(),
+ static_cast(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items")
+inline int poll(std::vector const &items, long timeout_ = -1)
+{
+ return poll(const_cast(items.data()), items.size(), timeout_);
+}
+
+inline int
+poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout)
+{
+ return poll(items, nitems, static_cast(timeout.count()));
+}
+
+inline int poll(std::vector &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(items.data(), items.size(), static_cast(timeout.count()));
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono instead of long")
+inline int poll(std::vector &items, long timeout_ = -1)
+{
+ return poll(items.data(), items.size(), timeout_);
+}
+
+template
+inline int poll(std::array &items,
+ std::chrono::milliseconds timeout)
+{
+ return poll(items.data(), items.size(), static_cast(timeout.count()));
+}
+#endif
+
+
+inline void version(int *major_, int *minor_, int *patch_)
+{
+ zmq_version(major_, minor_, patch_);
+}
+
+#ifdef ZMQ_CPP11
+inline std::tuple version()
+{
+ std::tuple v;
+ zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v));
+ return v;
+}
+
+#if !defined(ZMQ_CPP11_PARTIAL)
+namespace detail
+{
+template struct is_char_type
+{
+ // true if character type for string literals in C++11
+ static constexpr bool value =
+ std::is_same::value || std::is_same::value
+ || std::is_same::value || std::is_same::value;
+};
+}
+#endif
+
+#endif
+
+class message_t
+{
+ public:
+ message_t() ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_init(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ explicit message_t(size_t size_)
+ {
+ int rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ template message_t(ForwardIter first, ForwardIter last)
+ {
+ typedef typename std::iterator_traits::value_type value_t;
+
+ assert(std::distance(first, last) >= 0);
+ size_t const size_ =
+ static_cast(std::distance(first, last)) * sizeof(value_t);
+ int const rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ std::copy(first, last, data());
+ }
+
+ message_t(const void *data_, size_t size_)
+ {
+ int rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ if (size_) {
+ // this constructor allows (nullptr, 0),
+ // memcpy with a null pointer is UB
+ memcpy(data(), data_, size_);
+ }
+ }
+
+ message_t(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
+ {
+ int rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ // overload set of string-like types and generic containers
+#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
+ // NOTE this constructor will include the null terminator
+ // when called with a string literal.
+ // An overload taking const char* can not be added because
+ // it would be preferred over this function and break compatiblity.
+ template<
+ class Char,
+ size_t N,
+ typename = typename std::enable_if::value>::type>
+ ZMQ_DEPRECATED("from 4.7.0, use constructors taking iterators, (pointer, size) "
+ "or strings instead")
+ explicit message_t(const Char (&data)[N]) :
+ message_t(detail::ranges::begin(data), detail::ranges::end(data))
+ {
+ }
+
+ template::value
+ && ZMQ_IS_TRIVIALLY_COPYABLE(detail::range_value_t)
+ && !detail::is_char_type>::value
+ && !std::is_same::value>::type>
+ explicit message_t(const Range &rng) :
+ message_t(detail::ranges::begin(rng), detail::ranges::end(rng))
+ {
+ }
+
+ explicit message_t(const std::string &str) : message_t(str.data(), str.size()) {}
+
+#if CPPZMQ_HAS_STRING_VIEW
+ explicit message_t(std::string_view str) : message_t(str.data(), str.size()) {}
+#endif
+
+#endif
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ message_t(message_t &&rhs) ZMQ_NOTHROW : msg(rhs.msg)
+ {
+ int rc = zmq_msg_init(&rhs.msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ message_t &operator=(message_t &&rhs) ZMQ_NOTHROW
+ {
+ std::swap(msg, rhs.msg);
+ return *this;
+ }
+#endif
+
+ ~message_t() ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_close(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ void rebuild()
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init(&msg);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ void rebuild(size_t size_)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void rebuild(const void *data_, size_t size_)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_size(&msg, size_);
+ if (rc != 0)
+ throw error_t();
+ memcpy(data(), data_, size_);
+ }
+
+ void rebuild(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR)
+ {
+ int rc = zmq_msg_close(&msg);
+ if (rc != 0)
+ throw error_t();
+ rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_DEPRECATED("from 4.3.1, use move taking non-const reference instead")
+ void move(message_t const *msg_)
+ {
+ int rc = zmq_msg_move(&msg, const_cast(msg_->handle()));
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void move(message_t &msg_)
+ {
+ int rc = zmq_msg_move(&msg, msg_.handle());
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_DEPRECATED("from 4.3.1, use copy taking non-const reference instead")
+ void copy(message_t const *msg_)
+ {
+ int rc = zmq_msg_copy(&msg, const_cast(msg_->handle()));
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void copy(message_t &msg_)
+ {
+ int rc = zmq_msg_copy(&msg, msg_.handle());
+ if (rc != 0)
+ throw error_t();
+ }
+
+ bool more() const ZMQ_NOTHROW
+ {
+ int rc = zmq_msg_more(const_cast(&msg));
+ return rc != 0;
+ }
+
+ void *data() ZMQ_NOTHROW { return zmq_msg_data(&msg); }
+
+ const void *data() const ZMQ_NOTHROW
+ {
+ return zmq_msg_data(const_cast(&msg));
+ }
+
+ size_t size() const ZMQ_NOTHROW
+ {
+ return zmq_msg_size(const_cast(&msg));
+ }
+
+ ZMQ_NODISCARD bool empty() const ZMQ_NOTHROW { return size() == 0u; }
+
+ template T *data() ZMQ_NOTHROW { return static_cast(data()); }
+
+ template T const *data() const ZMQ_NOTHROW
+ {
+ return static_cast(data());
+ }
+
+ ZMQ_DEPRECATED("from 4.3.0, use operator== instead")
+ bool equal(const message_t *other) const ZMQ_NOTHROW { return *this == *other; }
+
+ bool operator==(const message_t &other) const ZMQ_NOTHROW
+ {
+ const size_t my_size = size();
+ return my_size == other.size() && 0 == memcmp(data(), other.data(), my_size);
+ }
+
+ bool operator!=(const message_t &other) const ZMQ_NOTHROW
+ {
+ return !(*this == other);
+ }
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0)
+ int get(int property_)
+ {
+ int value = zmq_msg_get(&msg, property_);
+ if (value == -1)
+ throw error_t();
+ return value;
+ }
+#endif
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0)
+ const char *gets(const char *property_)
+ {
+ const char *value = zmq_msg_gets(&msg, property_);
+ if (value == ZMQ_NULLPTR)
+ throw error_t();
+ return value;
+ }
+#endif
+
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ uint32_t routing_id() const
+ {
+ return zmq_msg_routing_id(const_cast(&msg));
+ }
+
+ void set_routing_id(uint32_t routing_id)
+ {
+ int rc = zmq_msg_set_routing_id(&msg, routing_id);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ const char *group() const
+ {
+ return zmq_msg_group(const_cast(&msg));
+ }
+
+ void set_group(const char *group)
+ {
+ int rc = zmq_msg_set_group(&msg, group);
+ if (rc != 0)
+ throw error_t();
+ }
+#endif
+
+ // interpret message content as a string
+ std::string to_string() const
+ {
+ return std::string(static_cast(data()), size());
+ }
+#if CPPZMQ_HAS_STRING_VIEW
+ // interpret message content as a string
+ std::string_view to_string_view() const noexcept
+ {
+ return std::string_view(static_cast(data()), size());
+ }
+#endif
+
+ /** Dump content to string for debugging.
+ * Ascii chars are readable, the rest is printed as hex.
+ * Probably ridiculously slow.
+ * Use to_string() or to_string_view() for
+ * interpreting the message as a string.
+ */
+ std::string str() const
+ {
+ // Partly mutuated from the same method in zmq::multipart_t
+ std::stringstream os;
+
+ const unsigned char *msg_data = this->data();
+ unsigned char byte;
+ size_t size = this->size();
+ int is_ascii[2] = {0, 0};
+
+ os << "zmq::message_t [size " << std::dec << std::setw(3)
+ << std::setfill('0') << size << "] (";
+ // Totally arbitrary
+ if (size >= 1000) {
+ os << "... too big to print)";
+ } else {
+ while (size--) {
+ byte = *msg_data++;
+
+ is_ascii[1] = (byte >= 32 && byte < 127);
+ if (is_ascii[1] != is_ascii[0])
+ os << " "; // Separate text/non text
+
+ if (is_ascii[1]) {
+ os << byte;
+ } else {
+ os << std::hex << std::uppercase << std::setw(2)
+ << std::setfill('0') << static_cast(byte);
+ }
+ is_ascii[0] = is_ascii[1];
+ }
+ os << ")";
+ }
+ return os.str();
+ }
+
+ void swap(message_t &other) ZMQ_NOTHROW
+ {
+ // this assumes zmq::msg_t from libzmq is trivially relocatable
+ std::swap(msg, other.msg);
+ }
+
+ ZMQ_NODISCARD zmq_msg_t *handle() ZMQ_NOTHROW { return &msg; }
+ ZMQ_NODISCARD const zmq_msg_t *handle() const ZMQ_NOTHROW { return &msg; }
+
+ private:
+ // The underlying message
+ zmq_msg_t msg;
+
+ // Disable implicit message copying, so that users won't use shared
+ // messages (less efficient) without being aware of the fact.
+ message_t(const message_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const message_t &) ZMQ_DELETED_FUNCTION;
+};
+
+inline void swap(message_t &a, message_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+#ifdef ZMQ_CPP11
+enum class ctxopt
+{
+#ifdef ZMQ_BLOCKY
+ blocky = ZMQ_BLOCKY,
+#endif
+#ifdef ZMQ_IO_THREADS
+ io_threads = ZMQ_IO_THREADS,
+#endif
+#ifdef ZMQ_THREAD_SCHED_POLICY
+ thread_sched_policy = ZMQ_THREAD_SCHED_POLICY,
+#endif
+#ifdef ZMQ_THREAD_PRIORITY
+ thread_priority = ZMQ_THREAD_PRIORITY,
+#endif
+#ifdef ZMQ_THREAD_AFFINITY_CPU_ADD
+ thread_affinity_cpu_add = ZMQ_THREAD_AFFINITY_CPU_ADD,
+#endif
+#ifdef ZMQ_THREAD_AFFINITY_CPU_REMOVE
+ thread_affinity_cpu_remove = ZMQ_THREAD_AFFINITY_CPU_REMOVE,
+#endif
+#ifdef ZMQ_THREAD_NAME_PREFIX
+ thread_name_prefix = ZMQ_THREAD_NAME_PREFIX,
+#endif
+#ifdef ZMQ_MAX_MSGSZ
+ max_msgsz = ZMQ_MAX_MSGSZ,
+#endif
+#ifdef ZMQ_ZERO_COPY_RECV
+ zero_copy_recv = ZMQ_ZERO_COPY_RECV,
+#endif
+#ifdef ZMQ_MAX_SOCKETS
+ max_sockets = ZMQ_MAX_SOCKETS,
+#endif
+#ifdef ZMQ_SOCKET_LIMIT
+ socket_limit = ZMQ_SOCKET_LIMIT,
+#endif
+#ifdef ZMQ_IPV6
+ ipv6 = ZMQ_IPV6,
+#endif
+#ifdef ZMQ_MSG_T_SIZE
+ msg_t_size = ZMQ_MSG_T_SIZE
+#endif
+};
+#endif
+
+class context_t
+{
+ public:
+ context_t()
+ {
+ ptr = zmq_ctx_new();
+ if (ptr == ZMQ_NULLPTR)
+ throw error_t();
+ }
+
+
+ explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT)
+ {
+ ptr = zmq_ctx_new();
+ if (ptr == ZMQ_NULLPTR)
+ throw error_t();
+
+ int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_);
+ ZMQ_ASSERT(rc == 0);
+
+ rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ context_t(context_t &&rhs) ZMQ_NOTHROW : ptr(rhs.ptr) { rhs.ptr = ZMQ_NULLPTR; }
+ context_t &operator=(context_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ std::swap(ptr, rhs.ptr);
+ return *this;
+ }
+#endif
+
+ ~context_t() ZMQ_NOTHROW { close(); }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use set taking zmq::ctxopt instead")
+ int setctxopt(int option_, int optval_)
+ {
+ int rc = zmq_ctx_set(ptr, option_, optval_);
+ ZMQ_ASSERT(rc == 0);
+ return rc;
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use get taking zmq::ctxopt instead")
+ int getctxopt(int option_) { return zmq_ctx_get(ptr, option_); }
+
+#ifdef ZMQ_CPP11
+ void set(ctxopt option, int optval)
+ {
+ int rc = zmq_ctx_set(ptr, static_cast(option), optval);
+ if (rc == -1)
+ throw error_t();
+ }
+
+ ZMQ_NODISCARD int get(ctxopt option)
+ {
+ int rc = zmq_ctx_get(ptr, static_cast(option));
+ // some options have a default value of -1
+ // which is unfortunate, and may result in errors
+ // that don't make sense
+ if (rc == -1)
+ throw error_t();
+ return rc;
+ }
+#endif
+
+ // Terminates context (see also shutdown()).
+ void close() ZMQ_NOTHROW
+ {
+ if (ptr == ZMQ_NULLPTR)
+ return;
+
+ int rc;
+ do {
+ rc = zmq_ctx_destroy(ptr);
+ } while (rc == -1 && errno == EINTR);
+
+ ZMQ_ASSERT(rc == 0);
+ ptr = ZMQ_NULLPTR;
+ }
+
+ // Shutdown context in preparation for termination (close()).
+ // Causes all blocking socket operations and any further
+ // socket operations to return with ETERM.
+ void shutdown() ZMQ_NOTHROW
+ {
+ if (ptr == ZMQ_NULLPTR)
+ return;
+ int rc = zmq_ctx_shutdown(ptr);
+ ZMQ_ASSERT(rc == 0);
+ }
+
+ // Be careful with this, it's probably only useful for
+ // using the C api together with an existing C++ api.
+ // Normally you should never need to use this.
+ ZMQ_EXPLICIT operator void *() ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_EXPLICIT operator void const *() const ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return ptr; }
+
+ ZMQ_DEPRECATED("from 4.7.0, use handle() != nullptr instead")
+ operator bool() const ZMQ_NOTHROW { return ptr != ZMQ_NULLPTR; }
+
+ void swap(context_t &other) ZMQ_NOTHROW { std::swap(ptr, other.ptr); }
+
+ private:
+ void *ptr;
+
+ context_t(const context_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const context_t &) ZMQ_DELETED_FUNCTION;
+};
+
+inline void swap(context_t &a, context_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+#ifdef ZMQ_CPP11
+
+struct recv_buffer_size
+{
+ size_t size; // number of bytes written to buffer
+ size_t untruncated_size; // untruncated message size in bytes
+
+ ZMQ_NODISCARD bool truncated() const noexcept
+ {
+ return size != untruncated_size;
+ }
+};
+
+#if CPPZMQ_HAS_OPTIONAL
+
+using send_result_t = std::optional;
+using recv_result_t = std::optional;
+using recv_buffer_result_t = std::optional;
+
+#else
+
+namespace detail
+{
+// A C++11 type emulating the most basic
+// operations of std::optional for trivial types
+template class trivial_optional
+{
+ public:
+ static_assert(std::is_trivial::value, "T must be trivial");
+ using value_type = T;
+
+ trivial_optional() = default;
+ trivial_optional(T value) noexcept : _value(value), _has_value(true) {}
+
+ const T *operator->() const noexcept
+ {
+ assert(_has_value);
+ return &_value;
+ }
+ T *operator->() noexcept
+ {
+ assert(_has_value);
+ return &_value;
+ }
+
+ const T &operator*() const noexcept
+ {
+ assert(_has_value);
+ return _value;
+ }
+ T &operator*() noexcept
+ {
+ assert(_has_value);
+ return _value;
+ }
+
+ T &value()
+ {
+ if (!_has_value)
+ throw std::exception();
+ return _value;
+ }
+ const T &value() const
+ {
+ if (!_has_value)
+ throw std::exception();
+ return _value;
+ }
+
+ explicit operator bool() const noexcept { return _has_value; }
+ bool has_value() const noexcept { return _has_value; }
+
+ private:
+ T _value{};
+ bool _has_value{false};
+};
+} // namespace detail
+
+using send_result_t = detail::trivial_optional;
+using recv_result_t = detail::trivial_optional;
+using recv_buffer_result_t = detail::trivial_optional;
+
+#endif
+
+namespace detail
+{
+template constexpr T enum_bit_or(T a, T b) noexcept
+{
+ static_assert(std::is_enum::value, "must be enum");
+ using U = typename std::underlying_type::type;
+ return static_cast(static_cast(a) | static_cast(b));
+}
+template constexpr T enum_bit_and(T a, T b) noexcept
+{
+ static_assert(std::is_enum::value, "must be enum");
+ using U = typename std::underlying_type::type;
+ return static_cast(static_cast(a) & static_cast(b));
+}
+template constexpr T enum_bit_xor(T a, T b) noexcept
+{
+ static_assert(std::is_enum::value, "must be enum");
+ using U = typename std::underlying_type::type;
+ return static_cast(static_cast(a) ^ static_cast(b));
+}
+template constexpr T enum_bit_not(T a) noexcept
+{
+ static_assert(std::is_enum::value, "must be enum");
+ using U = typename std::underlying_type::type;
+ return static_cast(~static_cast(a));
+}
+} // namespace detail
+
+// partially satisfies named requirement BitmaskType
+enum class send_flags : int
+{
+ none = 0,
+ dontwait = ZMQ_DONTWAIT,
+ sndmore = ZMQ_SNDMORE
+};
+
+constexpr send_flags operator|(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr send_flags operator&(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr send_flags operator^(send_flags a, send_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr send_flags operator~(send_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+// partially satisfies named requirement BitmaskType
+enum class recv_flags : int
+{
+ none = 0,
+ dontwait = ZMQ_DONTWAIT
+};
+
+constexpr recv_flags operator|(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr recv_flags operator&(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr recv_flags operator^(recv_flags a, recv_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr recv_flags operator~(recv_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+
+// mutable_buffer, const_buffer and buffer are based on
+// the Networking TS specification, draft:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf
+
+class mutable_buffer
+{
+ public:
+ constexpr mutable_buffer() noexcept : _data(nullptr), _size(0) {}
+ constexpr mutable_buffer(void *p, size_t n) noexcept : _data(p), _size(n)
+ {
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(p != nullptr || n == 0);
+#endif
+ }
+
+ constexpr void *data() const noexcept { return _data; }
+ constexpr size_t size() const noexcept { return _size; }
+ mutable_buffer &operator+=(size_t n) noexcept
+ {
+ // (std::min) is a workaround for when a min macro is defined
+ const auto shift = (std::min)(n, _size);
+ _data = static_cast(_data) + shift;
+ _size -= shift;
+ return *this;
+ }
+
+ private:
+ void *_data;
+ size_t _size;
+};
+
+inline mutable_buffer operator+(const mutable_buffer &mb, size_t n) noexcept
+{
+ return mutable_buffer(static_cast(mb.data()) + (std::min)(n, mb.size()),
+ mb.size() - (std::min)(n, mb.size()));
+}
+inline mutable_buffer operator+(size_t n, const mutable_buffer &mb) noexcept
+{
+ return mb + n;
+}
+
+class const_buffer
+{
+ public:
+ constexpr const_buffer() noexcept : _data(nullptr), _size(0) {}
+ constexpr const_buffer(const void *p, size_t n) noexcept : _data(p), _size(n)
+ {
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(p != nullptr || n == 0);
+#endif
+ }
+ constexpr const_buffer(const mutable_buffer &mb) noexcept :
+ _data(mb.data()),
+ _size(mb.size())
+ {
+ }
+
+ constexpr const void *data() const noexcept { return _data; }
+ constexpr size_t size() const noexcept { return _size; }
+ const_buffer &operator+=(size_t n) noexcept
+ {
+ const auto shift = (std::min)(n, _size);
+ _data = static_cast(_data) + shift;
+ _size -= shift;
+ return *this;
+ }
+
+ private:
+ const void *_data;
+ size_t _size;
+};
+
+inline const_buffer operator+(const const_buffer &cb, size_t n) noexcept
+{
+ return const_buffer(static_cast(cb.data())
+ + (std::min)(n, cb.size()),
+ cb.size() - (std::min)(n, cb.size()));
+}
+inline const_buffer operator+(size_t n, const const_buffer &cb) noexcept
+{
+ return cb + n;
+}
+
+// buffer creation
+
+constexpr mutable_buffer buffer(void *p, size_t n) noexcept
+{
+ return mutable_buffer(p, n);
+}
+constexpr const_buffer buffer(const void *p, size_t n) noexcept
+{
+ return const_buffer(p, n);
+}
+constexpr mutable_buffer buffer(const mutable_buffer &mb) noexcept
+{
+ return mb;
+}
+inline mutable_buffer buffer(const mutable_buffer &mb, size_t n) noexcept
+{
+ return mutable_buffer(mb.data(), (std::min)(mb.size(), n));
+}
+constexpr const_buffer buffer(const const_buffer &cb) noexcept
+{
+ return cb;
+}
+inline const_buffer buffer(const const_buffer &cb, size_t n) noexcept
+{
+ return const_buffer(cb.data(), (std::min)(cb.size(), n));
+}
+
+namespace detail
+{
+template struct is_buffer
+{
+ static constexpr bool value =
+ std::is_same::value || std::is_same::value;
+};
+
+template struct is_pod_like
+{
+ // NOTE: The networking draft N4771 section 16.11 requires
+ // T in the buffer functions below to be
+ // trivially copyable OR standard layout.
+ // Here we decide to be conservative and require both.
+ static constexpr bool value =
+ ZMQ_IS_TRIVIALLY_COPYABLE(T) && std::is_standard_layout::value;
+};
+
+template constexpr auto seq_size(const C &c) noexcept -> decltype(c.size())
+{
+ return c.size();
+}
+template
+constexpr size_t seq_size(const T (&/*array*/)[N]) noexcept
+{
+ return N;
+}
+
+template
+auto buffer_contiguous_sequence(Seq &&seq) noexcept
+ -> decltype(buffer(std::addressof(*std::begin(seq)), size_t{}))
+{
+ using T = typename std::remove_cv<
+ typename std::remove_reference::type>::type;
+ static_assert(detail::is_pod_like::value, "T must be POD");
+
+ const auto size = seq_size(seq);
+ return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
+ size * sizeof(T));
+}
+template
+auto buffer_contiguous_sequence(Seq &&seq, size_t n_bytes) noexcept
+ -> decltype(buffer_contiguous_sequence(seq))
+{
+ using T = typename std::remove_cv<
+ typename std::remove_reference::type>::type;
+ static_assert(detail::is_pod_like::value, "T must be POD");
+
+ const auto size = seq_size(seq);
+ return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr,
+ (std::min)(size * sizeof(T), n_bytes));
+}
+
+} // namespace detail
+
+// C array
+template mutable_buffer buffer(T (&data)[N]) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+mutable_buffer buffer(T (&data)[N], size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template const_buffer buffer(const T (&data)[N]) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(const T (&data)[N], size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::array
+template mutable_buffer buffer(std::array &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+mutable_buffer buffer(std::array &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template
+const_buffer buffer(std::array &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(std::array &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template
+const_buffer buffer(const std::array &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(const std::array &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::vector
+template
+mutable_buffer buffer(std::vector &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+mutable_buffer buffer(std::vector &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template
+const_buffer buffer(const std::vector &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(const std::vector &data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+// std::basic_string
+template
+mutable_buffer buffer(std::basic_string &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+mutable_buffer buffer(std::basic_string &data,
+ size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+template
+const_buffer buffer(const std::basic_string &data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(const std::basic_string &data,
+ size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+
+#if CPPZMQ_HAS_STRING_VIEW
+// std::basic_string_view
+template
+const_buffer buffer(std::basic_string_view data) noexcept
+{
+ return detail::buffer_contiguous_sequence(data);
+}
+template
+const_buffer buffer(std::basic_string_view data, size_t n_bytes) noexcept
+{
+ return detail::buffer_contiguous_sequence(data, n_bytes);
+}
+#endif
+
+// Buffer for a string literal (null terminated)
+// where the buffer size excludes the terminating character.
+// Equivalent to zmq::buffer(std::string_view("...")).
+template
+constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept
+{
+ static_assert(detail::is_pod_like::value, "Char must be POD");
+#ifdef ZMQ_EXTENDED_CONSTEXPR
+ assert(data[N - 1] == Char{0});
+#endif
+ return const_buffer(static_cast(data), (N - 1) * sizeof(Char));
+}
+
+namespace literals
+{
+constexpr const_buffer operator"" _zbuf(const char *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char));
+}
+constexpr const_buffer operator"" _zbuf(const wchar_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(wchar_t));
+}
+constexpr const_buffer operator"" _zbuf(const char16_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char16_t));
+}
+constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcept
+{
+ return const_buffer(str, len * sizeof(char32_t));
+}
+}
+
+#endif // ZMQ_CPP11
+
+
+#ifdef ZMQ_CPP11
+namespace sockopt
+{
+// There are two types of options,
+// integral type with known compiler time size (int, bool, int64_t, uint64_t)
+// and arrays with dynamic size (strings, binary data).
+
+// BoolUnit: if true accepts values of type bool (but passed as T into libzmq)
+template struct integral_option
+{
+};
+
+// NullTerm:
+// 0: binary data
+// 1: null-terminated string (`getsockopt` size includes null)
+// 2: binary (size 32) or Z85 encoder string of size 41 (null included)
+template struct array_option
+{
+};
+
+#define ZMQ_DEFINE_INTEGRAL_OPT(OPT, NAME, TYPE) \
+ using NAME##_t = integral_option; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(OPT, NAME, TYPE) \
+ using NAME##_t = integral_option; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT(OPT, NAME) \
+ using NAME##_t = array_option; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT_BINARY(OPT, NAME) \
+ using NAME##_t = array_option; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+#define ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(OPT, NAME) \
+ using NAME##_t = array_option; \
+ ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {}
+
+// duplicate definition from libzmq 4.3.3
+#if defined _WIN32
+#if defined _WIN64
+typedef unsigned __int64 cppzmq_fd_t;
+#else
+typedef unsigned int cppzmq_fd_t;
+#endif
+#else
+typedef int cppzmq_fd_t;
+#endif
+
+#ifdef ZMQ_AFFINITY
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_AFFINITY, affinity, uint64_t);
+#endif
+#ifdef ZMQ_BACKLOG
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int);
+#endif
+#ifdef ZMQ_BINDTODEVICE
+ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice);
+#endif
+#ifdef ZMQ_CONFLATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int);
+#endif
+#ifdef ZMQ_CONNECT_ROUTING_ID
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_CONNECT_ROUTING_ID, connect_routing_id);
+#endif
+#ifdef ZMQ_CONNECT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_CONNECT_TIMEOUT, connect_timeout, int);
+#endif
+#ifdef ZMQ_CURVE_PUBLICKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_PUBLICKEY, curve_publickey);
+#endif
+#ifdef ZMQ_CURVE_SECRETKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SECRETKEY, curve_secretkey);
+#endif
+#ifdef ZMQ_CURVE_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int);
+#endif
+#ifdef ZMQ_CURVE_SERVERKEY
+ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey);
+#endif
+#ifdef ZMQ_EVENTS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int);
+#endif
+#ifdef ZMQ_FD
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_FD, fd, cppzmq_fd_t);
+#endif
+#ifdef ZMQ_GSSAPI_PLAINTEXT
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_PLAINTEXT, gssapi_plaintext, int);
+#endif
+#ifdef ZMQ_GSSAPI_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_SERVER, gssapi_server, int);
+#endif
+#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL, gssapi_service_principal);
+#endif
+#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE,
+ gssapi_service_principal_nametype,
+ int);
+#endif
+#ifdef ZMQ_GSSAPI_PRINCIPAL
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_PRINCIPAL, gssapi_principal);
+#endif
+#ifdef ZMQ_GSSAPI_PRINCIPAL_NAMETYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_PRINCIPAL_NAMETYPE,
+ gssapi_principal_nametype,
+ int);
+#endif
+#ifdef ZMQ_HANDSHAKE_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HANDSHAKE_IVL, handshake_ivl, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_IVL, heartbeat_ivl, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int);
+#endif
+#ifdef ZMQ_HEARTBEAT_TTL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int);
+#endif
+#ifdef ZMQ_IMMEDIATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int);
+#endif
+#ifdef ZMQ_INVERT_MATCHING
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_INVERT_MATCHING, invert_matching, int);
+#endif
+#ifdef ZMQ_IPV6
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IPV6, ipv6, int);
+#endif
+#ifdef ZMQ_LAST_ENDPOINT
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_LAST_ENDPOINT, last_endpoint);
+#endif
+#ifdef ZMQ_LINGER
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_LINGER, linger, int);
+#endif
+#ifdef ZMQ_MAXMSGSIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MAXMSGSIZE, maxmsgsize, int64_t);
+#endif
+#ifdef ZMQ_MECHANISM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MECHANISM, mechanism, int);
+#endif
+#ifdef ZMQ_METADATA
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_METADATA, metadata);
+#endif
+#ifdef ZMQ_MULTICAST_HOPS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_HOPS, multicast_hops, int);
+#endif
+#ifdef ZMQ_MULTICAST_LOOP
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int);
+#endif
+#ifdef ZMQ_MULTICAST_MAXTPDU
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int);
+#endif
+#ifdef ZMQ_PLAIN_SERVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int);
+#endif
+#ifdef ZMQ_PLAIN_PASSWORD
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password);
+#endif
+#ifdef ZMQ_PLAIN_USERNAME
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username);
+#endif
+#ifdef ZMQ_USE_FD
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int);
+#endif
+#ifdef ZMQ_PROBE_ROUTER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PROBE_ROUTER, probe_router, int);
+#endif
+#ifdef ZMQ_RATE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RATE, rate, int);
+#endif
+#ifdef ZMQ_RCVBUF
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVBUF, rcvbuf, int);
+#endif
+#ifdef ZMQ_RCVHWM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVHWM, rcvhwm, int);
+#endif
+#ifdef ZMQ_RCVMORE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_RCVMORE, rcvmore, int);
+#endif
+#ifdef ZMQ_RCVTIMEO
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVTIMEO, rcvtimeo, int);
+#endif
+#ifdef ZMQ_RECONNECT_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int);
+#endif
+#ifdef ZMQ_RECONNECT_IVL_MAX
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int);
+#endif
+#ifdef ZMQ_RECOVERY_IVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int);
+#endif
+#ifdef ZMQ_REQ_CORRELATE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_CORRELATE, req_correlate, int);
+#endif
+#ifdef ZMQ_REQ_RELAXED
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_RELAXED, req_relaxed, int);
+#endif
+#ifdef ZMQ_ROUTER_HANDOVER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_HANDOVER, router_handover, int);
+#endif
+#ifdef ZMQ_ROUTER_MANDATORY
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int);
+#endif
+#ifdef ZMQ_ROUTER_NOTIFY
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int);
+#endif
+#ifdef ZMQ_ROUTING_ID
+ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id);
+#endif
+#ifdef ZMQ_SNDBUF
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDBUF, sndbuf, int);
+#endif
+#ifdef ZMQ_SNDHWM
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int);
+#endif
+#ifdef ZMQ_SNDTIMEO
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int);
+#endif
+#ifdef ZMQ_SOCKS_PROXY
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy);
+#endif
+#ifdef ZMQ_STREAM_NOTIFY
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int);
+#endif
+#ifdef ZMQ_SUBSCRIBE
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_SUBSCRIBE, subscribe);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE, tcp_keepalive, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_CNT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_CNT, tcp_keepalive_cnt, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_IDLE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_IDLE, tcp_keepalive_idle, int);
+#endif
+#ifdef ZMQ_TCP_KEEPALIVE_INTVL
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_INTVL, tcp_keepalive_intvl, int);
+#endif
+#ifdef ZMQ_TCP_MAXRT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_MAXRT, tcp_maxrt, int);
+#endif
+#ifdef ZMQ_THREAD_SAFE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_THREAD_SAFE, thread_safe, int);
+#endif
+#ifdef ZMQ_TOS
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int);
+#endif
+#ifdef ZMQ_TYPE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int);
+#endif
+#ifdef ZMQ_UNSUBSCRIBE
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_SIZE, vmci_buffer_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MIN_SIZE, vmci_buffer_min_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MAX_SIZE, vmci_buffer_max_size, uint64_t);
+#endif
+#ifdef ZMQ_VMCI_CONNECT_TIMEOUT
+ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_CONNECT_TIMEOUT, vmci_connect_timeout, int);
+#endif
+#ifdef ZMQ_XPUB_VERBOSE
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSE, xpub_verbose, int);
+#endif
+#ifdef ZMQ_XPUB_VERBOSER
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int);
+#endif
+#ifdef ZMQ_XPUB_MANUAL
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int);
+#endif
+#ifdef ZMQ_XPUB_NODROP
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int);
+#endif
+#ifdef ZMQ_XPUB_WELCOME_MSG
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_XPUB_WELCOME_MSG, xpub_welcome_msg);
+#endif
+#ifdef ZMQ_ZAP_ENFORCE_DOMAIN
+ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ZAP_ENFORCE_DOMAIN, zap_enforce_domain, int);
+#endif
+#ifdef ZMQ_ZAP_DOMAIN
+ZMQ_DEFINE_ARRAY_OPT(ZMQ_ZAP_DOMAIN, zap_domain);
+#endif
+
+} // namespace sockopt
+#endif // ZMQ_CPP11
+
+
+namespace detail
+{
+class socket_base
+{
+ public:
+ socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) {}
+ ZMQ_EXPLICIT socket_base(void *handle) ZMQ_NOTHROW : _handle(handle) {}
+
+ template
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
+ void setsockopt(int option_, T const &optval)
+ {
+ setsockopt(option_, &optval, sizeof(T));
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt")
+ void setsockopt(int option_, const void *optval_, size_t optvallen_)
+ {
+ int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
+ void getsockopt(int option_, void *optval_, size_t *optvallen_) const
+ {
+ int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ template
+ ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt")
+ T getsockopt(int option_) const
+ {
+ T optval;
+ size_t optlen = sizeof(T);
+ getsockopt(option_, &optval, &optlen);
+ return optval;
+ }
+
+#ifdef ZMQ_CPP11
+ // Set integral socket option, e.g.
+ // `socket.set(zmq::sockopt::linger, 0)`
+ template
+ void set(sockopt::integral_option, const T &val)
+ {
+ static_assert(std::is_integral::value, "T must be integral");
+ set_option(Opt, &val, sizeof val);
+ }
+
+ // Set integral socket option from boolean, e.g.
+ // `socket.set(zmq::sockopt::immediate, false)`
+ template
+ void set(sockopt::integral_option, bool val)
+ {
+ static_assert(std::is_integral::value, "T must be integral");
+ T rep_val = val;
+ set_option(Opt, &rep_val, sizeof rep_val);
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::plain_username, "foo123")`
+ template
+ void set(sockopt::array_option, const char *buf)
+ {
+ set_option(Opt, buf, std::strlen(buf));
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, zmq::buffer(id))`
+ template
+ void set(sockopt::array_option, const_buffer buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, id_str)`
+ template
+ void set(sockopt::array_option, const std::string &buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+
+#if CPPZMQ_HAS_STRING_VIEW
+ // Set array socket option, e.g.
+ // `socket.set(zmq::sockopt::routing_id, id_str)`
+ template
+ void set(sockopt::array_option, std::string_view buf)
+ {
+ set_option(Opt, buf.data(), buf.size());
+ }
+#endif
+
+ // Get scalar socket option, e.g.
+ // `auto opt = socket.get(zmq::sockopt::linger)`
+ template
+ ZMQ_NODISCARD T get(sockopt::integral_option) const
+ {
+ static_assert(std::is_integral::value, "T must be integral");
+ T val;
+ size_t size = sizeof val;
+ get_option(Opt, &val, &size);
+ assert(size == sizeof val);
+ return val;
+ }
+
+ // Get array socket option, writes to buf, returns option size in bytes, e.g.
+ // `size_t optsize = socket.get(zmq::sockopt::routing_id, zmq::buffer(id))`
+ template
+ ZMQ_NODISCARD size_t get(sockopt::array_option,
+ mutable_buffer buf) const
+ {
+ size_t size = buf.size();
+ get_option(Opt, buf.data(), &size);
+ return size;
+ }
+
+ // Get array socket option as string (initializes the string buffer size to init_size) e.g.
+ // `auto s = socket.get(zmq::sockopt::routing_id)`
+ // Note: removes the null character from null-terminated string options,
+ // i.e. the string size excludes the null character.
+ template
+ ZMQ_NODISCARD std::string get(sockopt::array_option,
+ size_t init_size = 1024) const
+ {
+ if (NullTerm == 2 && init_size == 1024) {
+ init_size = 41; // get as Z85 string
+ }
+ std::string str(init_size, '\0');
+ size_t size = get(sockopt::array_option{}, buffer(str));
+ if (NullTerm == 1) {
+ if (size > 0) {
+ assert(str[size - 1] == '\0');
+ --size;
+ }
+ } else if (NullTerm == 2) {
+ assert(size == 32 || size == 41);
+ if (size == 41) {
+ assert(str[size - 1] == '\0');
+ --size;
+ }
+ }
+ str.resize(size);
+ return str;
+ }
+#endif
+
+ void bind(std::string const &addr) { bind(addr.c_str()); }
+
+ void bind(const char *addr_)
+ {
+ int rc = zmq_bind(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void unbind(std::string const &addr) { unbind(addr.c_str()); }
+
+ void unbind(const char *addr_)
+ {
+ int rc = zmq_unbind(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void connect(std::string const &addr) { connect(addr.c_str()); }
+
+ void connect(const char *addr_)
+ {
+ int rc = zmq_connect(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void disconnect(std::string const &addr) { disconnect(addr.c_str()); }
+
+ void disconnect(const char *addr_)
+ {
+ int rc = zmq_disconnect(_handle, addr_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ bool connected() const ZMQ_NOTHROW { return (_handle != ZMQ_NULLPTR); }
+
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking a const_buffer and send_flags")
+ size_t send(const void *buf_, size_t len_, int flags_ = 0)
+ {
+ int nbytes = zmq_send(_handle, buf_, len_, flags_);
+ if (nbytes >= 0)
+ return static_cast(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return 0;
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
+ bool send(message_t &msg_,
+ int flags_ = 0) // default until removed
+ {
+ int nbytes = zmq_msg_send(msg_.handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+ template
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.4.1, use send taking message_t or buffer (for contiguous "
+ "ranges), and send_flags")
+ bool send(T first, T last, int flags_ = 0)
+ {
+ zmq::message_t msg(first, last);
+ int nbytes = zmq_msg_send(msg.handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags")
+ bool send(message_t &&msg_,
+ int flags_ = 0) // default until removed
+ {
+#ifdef ZMQ_CPP11
+ return send(msg_, static_cast(flags_)).has_value();
+#else
+ return send(msg_, flags_);
+#endif
+ }
+#endif
+
+#ifdef ZMQ_CPP11
+ send_result_t send(const_buffer buf, send_flags flags = send_flags::none)
+ {
+ const int nbytes =
+ zmq_send(_handle, buf.data(), buf.size(), static_cast(flags));
+ if (nbytes >= 0)
+ return static_cast(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ send_result_t send(message_t &msg, send_flags flags)
+ {
+ int nbytes = zmq_msg_send(msg.handle(), _handle, static_cast(flags));
+ if (nbytes >= 0)
+ return static_cast(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ send_result_t send(message_t &&msg, send_flags flags)
+ {
+ return send(msg, flags);
+ }
+#endif
+
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.3.1, use recv taking a mutable_buffer and recv_flags")
+ size_t recv(void *buf_, size_t len_, int flags_ = 0)
+ {
+ int nbytes = zmq_recv(_handle, buf_, len_, flags_);
+ if (nbytes >= 0)
+ return static_cast(nbytes);
+ if (zmq_errno() == EAGAIN)
+ return 0;
+ throw error_t();
+ }
+
+ ZMQ_CPP11_DEPRECATED(
+ "from 4.3.1, use recv taking a reference to message_t and recv_flags")
+ bool recv(message_t *msg_, int flags_ = 0)
+ {
+ int nbytes = zmq_msg_recv(msg_->handle(), _handle, flags_);
+ if (nbytes >= 0)
+ return true;
+ if (zmq_errno() == EAGAIN)
+ return false;
+ throw error_t();
+ }
+
+#ifdef ZMQ_CPP11
+ ZMQ_NODISCARD
+ recv_buffer_result_t recv(mutable_buffer buf,
+ recv_flags flags = recv_flags::none)
+ {
+ const int nbytes =
+ zmq_recv(_handle, buf.data(), buf.size(), static_cast(flags));
+ if (nbytes >= 0) {
+ return recv_buffer_size{
+ (std::min)(static_cast(nbytes), buf.size()),
+ static_cast(nbytes)};
+ }
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+
+ ZMQ_NODISCARD
+ recv_result_t recv(message_t &msg, recv_flags flags = recv_flags::none)
+ {
+ const int nbytes =
+ zmq_msg_recv(msg.handle(), _handle, static_cast(flags));
+ if (nbytes >= 0) {
+ assert(msg.size() == static_cast(nbytes));
+ return static_cast(nbytes);
+ }
+ if (zmq_errno() == EAGAIN)
+ return {};
+ throw error_t();
+ }
+#endif
+
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ void join(const char *group)
+ {
+ int rc = zmq_join(_handle, group);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void leave(const char *group)
+ {
+ int rc = zmq_leave(_handle, group);
+ if (rc != 0)
+ throw error_t();
+ }
+#endif
+
+ ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return _handle; }
+ ZMQ_NODISCARD const void *handle() const ZMQ_NOTHROW { return _handle; }
+
+ ZMQ_EXPLICIT operator bool() const ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
+ // note: non-const operator bool can be removed once
+ // operator void* is removed from socket_t
+ ZMQ_EXPLICIT operator bool() ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; }
+
+ protected:
+ void *_handle;
+
+ private:
+ void set_option(int option_, const void *optval_, size_t optvallen_)
+ {
+ int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+
+ void get_option(int option_, void *optval_, size_t *optvallen_) const
+ {
+ int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_);
+ if (rc != 0)
+ throw error_t();
+ }
+};
+} // namespace detail
+
+#ifdef ZMQ_CPP11
+enum class socket_type : int
+{
+ req = ZMQ_REQ,
+ rep = ZMQ_REP,
+ dealer = ZMQ_DEALER,
+ router = ZMQ_ROUTER,
+ pub = ZMQ_PUB,
+ sub = ZMQ_SUB,
+ xpub = ZMQ_XPUB,
+ xsub = ZMQ_XSUB,
+ push = ZMQ_PUSH,
+ pull = ZMQ_PULL,
+#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
+ server = ZMQ_SERVER,
+ client = ZMQ_CLIENT,
+ radio = ZMQ_RADIO,
+ dish = ZMQ_DISH,
+#endif
+#if ZMQ_VERSION_MAJOR >= 4
+ stream = ZMQ_STREAM,
+#endif
+ pair = ZMQ_PAIR
+};
+#endif
+
+struct from_handle_t
+{
+ struct _private
+ {
+ }; // disabling use other than with from_handle
+ ZMQ_CONSTEXPR_FN ZMQ_EXPLICIT from_handle_t(_private /*p*/) ZMQ_NOTHROW {}
+};
+
+ZMQ_CONSTEXPR_VAR from_handle_t from_handle =
+ from_handle_t(from_handle_t::_private());
+
+// A non-owning nullable reference to a socket.
+// The reference is invalidated on socket close or destruction.
+class socket_ref : public detail::socket_base
+{
+ public:
+ socket_ref() ZMQ_NOTHROW : detail::socket_base() {}
+#ifdef ZMQ_CPP11
+ socket_ref(std::nullptr_t) ZMQ_NOTHROW : detail::socket_base() {}
+#endif
+ socket_ref(from_handle_t /*fh*/, void *handle) ZMQ_NOTHROW
+ : detail::socket_base(handle)
+ {
+ }
+};
+
+#ifdef ZMQ_CPP11
+inline bool operator==(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
+{
+ return sr.handle() == nullptr;
+}
+inline bool operator==(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
+{
+ return sr.handle() == nullptr;
+}
+inline bool operator!=(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW
+{
+ return !(sr == nullptr);
+}
+inline bool operator!=(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW
+{
+ return !(sr == nullptr);
+}
+#endif
+
+inline bool operator==(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return std::equal_to()(a.handle(), b.handle());
+}
+inline bool operator!=(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return !(a == b);
+}
+inline bool operator<(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return std::less()(a.handle(), b.handle());
+}
+inline bool operator>(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return b < a;
+}
+inline bool operator<=(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return !(a > b);
+}
+inline bool operator>=(socket_ref a, socket_ref b) ZMQ_NOTHROW
+{
+ return !(a < b);
+}
+
+} // namespace zmq
+
+#ifdef ZMQ_CPP11
+namespace std
+{
+template<> struct hash
+{
+ size_t operator()(zmq::socket_ref sr) const ZMQ_NOTHROW
+ {
+ return hash()(sr.handle());
+ }
+};
+} // namespace std
+#endif
+
+namespace zmq
+{
+class socket_t : public detail::socket_base
+{
+ friend class monitor_t;
+
+ public:
+ socket_t() ZMQ_NOTHROW : detail::socket_base(ZMQ_NULLPTR), ctxptr(ZMQ_NULLPTR) {}
+
+ socket_t(context_t &context_, int type_) :
+ detail::socket_base(zmq_socket(context_.handle(), type_)),
+ ctxptr(context_.handle())
+ {
+ if (_handle == ZMQ_NULLPTR)
+ throw error_t();
+ }
+
+#ifdef ZMQ_CPP11
+ socket_t(context_t &context_, socket_type type_) :
+ socket_t(context_, static_cast(type_))
+ {
+ }
+#endif
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ socket_t(socket_t &&rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle),
+ ctxptr(rhs.ctxptr)
+ {
+ rhs._handle = ZMQ_NULLPTR;
+ rhs.ctxptr = ZMQ_NULLPTR;
+ }
+ socket_t &operator=(socket_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ std::swap(_handle, rhs._handle);
+ std::swap(ctxptr, rhs.ctxptr);
+ return *this;
+ }
+#endif
+
+ ~socket_t() ZMQ_NOTHROW { close(); }
+
+ operator void *() ZMQ_NOTHROW { return _handle; }
+
+ operator void const *() const ZMQ_NOTHROW { return _handle; }
+
+ void close() ZMQ_NOTHROW
+ {
+ if (_handle == ZMQ_NULLPTR)
+ // already closed
+ return;
+ int rc = zmq_close(_handle);
+ ZMQ_ASSERT(rc == 0);
+ _handle = ZMQ_NULLPTR;
+ ctxptr = ZMQ_NULLPTR;
+ }
+
+ void swap(socket_t &other) ZMQ_NOTHROW
+ {
+ std::swap(_handle, other._handle);
+ std::swap(ctxptr, other.ctxptr);
+ }
+
+ operator socket_ref() ZMQ_NOTHROW { return socket_ref(from_handle, _handle); }
+
+ private:
+ void *ctxptr;
+
+ socket_t(const socket_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const socket_t &) ZMQ_DELETED_FUNCTION;
+
+ // used by monitor_t
+ socket_t(void *context_, int type_) :
+ detail::socket_base(zmq_socket(context_, type_)),
+ ctxptr(context_)
+ {
+ if (_handle == ZMQ_NULLPTR)
+ throw error_t();
+ if (ctxptr == ZMQ_NULLPTR)
+ throw error_t();
+ }
+};
+
+inline void swap(socket_t &a, socket_t &b) ZMQ_NOTHROW
+{
+ a.swap(b);
+}
+
+ZMQ_DEPRECATED("from 4.3.1, use proxy taking socket_t objects")
+inline void proxy(void *frontend, void *backend, void *capture)
+{
+ int rc = zmq_proxy(frontend, backend, capture);
+ if (rc != 0)
+ throw error_t();
+}
+
+inline void
+proxy(socket_ref frontend, socket_ref backend, socket_ref capture = socket_ref())
+{
+ int rc = zmq_proxy(frontend.handle(), backend.handle(), capture.handle());
+ if (rc != 0)
+ throw error_t();
+}
+
+#ifdef ZMQ_HAS_PROXY_STEERABLE
+ZMQ_DEPRECATED("from 4.3.1, use proxy_steerable taking socket_t objects")
+inline void
+proxy_steerable(void *frontend, void *backend, void *capture, void *control)
+{
+ int rc = zmq_proxy_steerable(frontend, backend, capture, control);
+ if (rc != 0)
+ throw error_t();
+}
+
+inline void proxy_steerable(socket_ref frontend,
+ socket_ref backend,
+ socket_ref capture,
+ socket_ref control)
+{
+ int rc = zmq_proxy_steerable(frontend.handle(), backend.handle(),
+ capture.handle(), control.handle());
+ if (rc != 0)
+ throw error_t();
+}
+#endif
+
+class monitor_t
+{
+ public:
+ monitor_t() : _socket(), _monitor_socket() {}
+
+ virtual ~monitor_t() { close(); }
+
+#ifdef ZMQ_HAS_RVALUE_REFS
+ monitor_t(monitor_t &&rhs) ZMQ_NOTHROW : _socket(), _monitor_socket()
+ {
+ std::swap(_socket, rhs._socket);
+ std::swap(_monitor_socket, rhs._monitor_socket);
+ }
+
+ monitor_t &operator=(monitor_t &&rhs) ZMQ_NOTHROW
+ {
+ close();
+ _socket = socket_ref();
+ std::swap(_socket, rhs._socket);
+ std::swap(_monitor_socket, rhs._monitor_socket);
+ return *this;
+ }
+#endif
+
+
+ void
+ monitor(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
+ {
+ monitor(socket, addr.c_str(), events);
+ }
+
+ void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
+ {
+ init(socket, addr_, events);
+ while (true) {
+ check_event(-1);
+ }
+ }
+
+ void init(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL)
+ {
+ init(socket, addr.c_str(), events);
+ }
+
+ void init(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL)
+ {
+ int rc = zmq_socket_monitor(socket.handle(), addr_, events);
+ if (rc != 0)
+ throw error_t();
+
+ _socket = socket;
+ _monitor_socket = socket_t(socket.ctxptr, ZMQ_PAIR);
+ _monitor_socket.connect(addr_);
+
+ on_monitor_started();
+ }
+
+ bool check_event(int timeout = 0)
+ {
+ assert(_monitor_socket);
+
+ zmq_msg_t eventMsg;
+ zmq_msg_init(&eventMsg);
+
+ zmq::pollitem_t items[] = {
+ {_monitor_socket.handle(), 0, ZMQ_POLLIN, 0},
+ };
+
+ zmq::poll(&items[0], 1, timeout);
+
+ if (items[0].revents & ZMQ_POLLIN) {
+ int rc = zmq_msg_recv(&eventMsg, _monitor_socket.handle(), 0);
+ if (rc == -1 && zmq_errno() == ETERM)
+ return false;
+ assert(rc != -1);
+
+ } else {
+ zmq_msg_close(&eventMsg);
+ return false;
+ }
+
+#if ZMQ_VERSION_MAJOR >= 4
+ const char *data = static_cast(zmq_msg_data(&eventMsg));
+ zmq_event_t msgEvent;
+ memcpy(&msgEvent.event, data, sizeof(uint16_t));
+ data += sizeof(uint16_t);
+ memcpy(&msgEvent.value, data, sizeof(int32_t));
+ zmq_event_t *event = &msgEvent;
+#else
+ zmq_event_t *event = static_cast(zmq_msg_data(&eventMsg));
+#endif
+
+#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT
+ zmq_msg_t addrMsg;
+ zmq_msg_init(&addrMsg);
+ int rc = zmq_msg_recv(&addrMsg, _monitor_socket.handle(), 0);
+ if (rc == -1 && zmq_errno() == ETERM) {
+ zmq_msg_close(&eventMsg);
+ return false;
+ }
+
+ assert(rc != -1);
+ const char *str = static_cast(zmq_msg_data(&addrMsg));
+ std::string address(str, str + zmq_msg_size(&addrMsg));
+ zmq_msg_close(&addrMsg);
+#else
+ // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types.
+ std::string address = event->data.connected.addr;
+#endif
+
+#ifdef ZMQ_EVENT_MONITOR_STOPPED
+ if (event->event == ZMQ_EVENT_MONITOR_STOPPED) {
+ zmq_msg_close(&eventMsg);
+ return false;
+ }
+
+#endif
+
+ switch (event->event) {
+ case ZMQ_EVENT_CONNECTED:
+ on_event_connected(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CONNECT_DELAYED:
+ on_event_connect_delayed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CONNECT_RETRIED:
+ on_event_connect_retried(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_LISTENING:
+ on_event_listening(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_BIND_FAILED:
+ on_event_bind_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_ACCEPTED:
+ on_event_accepted(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_ACCEPT_FAILED:
+ on_event_accept_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CLOSED:
+ on_event_closed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_CLOSE_FAILED:
+ on_event_close_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_DISCONNECTED:
+ on_event_disconnected(*event, address.c_str());
+ break;
+#ifdef ZMQ_BUILD_DRAFT_API
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL:
+ on_event_handshake_failed_no_detail(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL:
+ on_event_handshake_failed_protocol(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH:
+ on_event_handshake_failed_auth(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_SUCCEEDED:
+ on_event_handshake_succeeded(*event, address.c_str());
+ break;
+#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
+ case ZMQ_EVENT_HANDSHAKE_FAILED:
+ on_event_handshake_failed(*event, address.c_str());
+ break;
+ case ZMQ_EVENT_HANDSHAKE_SUCCEED:
+ on_event_handshake_succeed(*event, address.c_str());
+ break;
+#endif
+#endif
+ default:
+ on_event_unknown(*event, address.c_str());
+ break;
+ }
+ zmq_msg_close(&eventMsg);
+
+ return true;
+ }
+
+#ifdef ZMQ_EVENT_MONITOR_STOPPED
+ void abort()
+ {
+ if (_socket)
+ zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
+
+ _socket = socket_ref();
+ }
+#endif
+ virtual void on_monitor_started() {}
+ virtual void on_event_connected(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_connect_delayed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_connect_retried(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_listening(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_bind_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_accepted(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_accept_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_closed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_close_failed(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_disconnected(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ virtual void on_event_handshake_failed_no_detail(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_failed_protocol(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_failed_auth(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_succeeded(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1)
+ virtual void on_event_handshake_failed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+ virtual void on_event_handshake_succeed(const zmq_event_t &event_,
+ const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+#endif
+ virtual void on_event_unknown(const zmq_event_t &event_, const char *addr_)
+ {
+ (void) event_;
+ (void) addr_;
+ }
+
+ private:
+ monitor_t(const monitor_t &) ZMQ_DELETED_FUNCTION;
+ void operator=(const monitor_t &) ZMQ_DELETED_FUNCTION;
+
+ socket_ref _socket;
+ socket_t _monitor_socket;
+
+ void close() ZMQ_NOTHROW
+ {
+ if (_socket)
+ zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0);
+ _monitor_socket.close();
+ }
+};
+
+#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
+
+// polling events
+enum class event_flags : short
+{
+ none = 0,
+ pollin = ZMQ_POLLIN,
+ pollout = ZMQ_POLLOUT,
+ pollerr = ZMQ_POLLERR,
+ pollpri = ZMQ_POLLPRI
+};
+
+constexpr event_flags operator|(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_or(a, b);
+}
+constexpr event_flags operator&(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_and(a, b);
+}
+constexpr event_flags operator^(event_flags a, event_flags b) noexcept
+{
+ return detail::enum_bit_xor(a, b);
+}
+constexpr event_flags operator~(event_flags a) noexcept
+{
+ return detail::enum_bit_not(a);
+}
+
+struct no_user_data;
+
+// layout compatible with zmq_poller_event_t
+template struct poller_event
+{
+ socket_ref socket;
+#ifdef _WIN32
+ SOCKET fd;
+#else
+ int fd;
+#endif
+ T *user_data;
+ event_flags events;
+};
+
+template class poller_t
+{
+ public:
+ using event_type = poller_event;
+
+ poller_t() : poller_ptr(zmq_poller_new())
+ {
+ if (!poller_ptr)
+ throw error_t();
+ }
+
+ template<
+ typename Dummy = void,
+ typename =
+ typename std::enable_if::value, Dummy>::type>
+ void add(zmq::socket_ref socket, event_flags events, T *user_data)
+ {
+ add_impl(socket, events, user_data);
+ }
+
+ void add(zmq::socket_ref socket, event_flags events)
+ {
+ add_impl(socket, events, nullptr);
+ }
+
+ void remove(zmq::socket_ref socket)
+ {
+ if (0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) {
+ throw error_t();
+ }
+ }
+
+ void modify(zmq::socket_ref socket, event_flags events)
+ {
+ if (0
+ != zmq_poller_modify(poller_ptr.get(), socket.handle(),
+ static_cast(events))) {
+ throw error_t();
+ }
+ }
+
+ size_t wait_all(std::vector &poller_events,
+ const std::chrono::milliseconds timeout)
+ {
+ int rc = zmq_poller_wait_all(
+ poller_ptr.get(),
+ reinterpret_cast(poller_events.data()),
+ static_cast(poller_events.size()),
+ static_cast(timeout.count()));
+ if (rc > 0)
+ return static_cast(rc);
+
+#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)
+ if (zmq_errno() == EAGAIN)
+#else
+ if (zmq_errno() == ETIMEDOUT)
+#endif
+ return 0;
+
+ throw error_t();
+ }
+
+ private:
+ struct destroy_poller_t
+ {
+ void operator()(void *ptr) noexcept
+ {
+ int rc = zmq_poller_destroy(&ptr);
+ ZMQ_ASSERT(rc == 0);
+ }
+ };
+
+ std::unique_ptr poller_ptr;
+
+ void add_impl(zmq::socket_ref socket, event_flags events, T *user_data)
+ {
+ if (0
+ != zmq_poller_add(poller_ptr.get(), socket.handle(), user_data,
+ static_cast(events))) {
+ throw error_t();
+ }
+ }
+};
+#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
+
+inline std::ostream &operator<<(std::ostream &os, const message_t &msg)
+{
+ return os << msg.str();
+}
+
+} // namespace zmq
+
+#endif // __ZMQ_HPP_INCLUDED__
diff --git a/include/zmq_addon.hpp b/include/zmq_addon.hpp
new file mode 100644
index 000000000..021ab9540
--- /dev/null
+++ b/include/zmq_addon.hpp
@@ -0,0 +1,743 @@
+/*
+ Copyright (c) 2016-2017 ZeroMQ community
+ Copyright (c) 2016 VOCA AS / Harald Nøkland
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+*/
+
+#ifndef __ZMQ_ADDON_HPP_INCLUDED__
+#define __ZMQ_ADDON_HPP_INCLUDED__
+
+#include "zmq.hpp"
+
+#include
+#include
+#include
+#include
+#ifdef ZMQ_CPP11
+#include
+#include
+#include
+#endif
+
+namespace zmq
+{
+#ifdef ZMQ_CPP11
+
+namespace detail
+{
+template
+recv_result_t
+recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags)
+{
+ size_t msg_count = 0;
+ message_t msg;
+ while (true) {
+ if (CheckN) {
+ if (msg_count >= n)
+ throw std::runtime_error(
+ "Too many message parts in recv_multipart_n");
+ }
+ if (!s.recv(msg, flags)) {
+ // zmq ensures atomic delivery of messages
+ assert(msg_count == 0);
+ return {};
+ }
+ ++msg_count;
+ const bool more = msg.more();
+ *out++ = std::move(msg);
+ if (!more)
+ break;
+ }
+ return msg_count;
+}
+
+inline bool is_little_endian()
+{
+ const uint16_t i = 0x01;
+ return *reinterpret_cast(&i) == 0x01;
+}
+
+inline void write_network_order(unsigned char *buf, const uint32_t value)
+{
+ if (is_little_endian()) {
+ ZMQ_CONSTEXPR_VAR uint32_t mask = std::numeric_limits::max();
+ *buf++ = (value >> 24) & mask;
+ *buf++ = (value >> 16) & mask;
+ *buf++ = (value >> 8) & mask;
+ *buf++ = value & mask;
+ } else {
+ std::memcpy(buf, &value, sizeof(value));
+ }
+}
+
+inline uint32_t read_u32_network_order(const unsigned char *buf)
+{
+ if (is_little_endian()) {
+ return (static_cast(buf[0]) << 24)
+ + (static_cast(buf[1]) << 16)
+ + (static_cast(buf[2]) << 8)
+ + static_cast(buf[3]);
+ } else {
+ uint32_t value;
+ std::memcpy(&value, buf, sizeof(value));
+ return value;
+ }
+}
+} // namespace detail
+
+/* Receive a multipart message.
+
+ Writes the zmq::message_t objects to OutputIterator out.
+ The out iterator must handle an unspecified number of writes,
+ e.g. by using std::back_inserter.
+
+ Returns: the number of messages received or nullopt (on EAGAIN).
+ Throws: if recv throws. Any exceptions thrown
+ by the out iterator will be propagated and the message
+ may have been only partially received with pending
+ message parts. It is adviced to close this socket in that event.
+*/
+template
+ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s,
+ OutputIt out,
+ recv_flags flags = recv_flags::none)
+{
+ return detail::recv_multipart_n(s, std::move(out), 0, flags);
+}
+
+/* Receive a multipart message.
+
+ Writes at most n zmq::message_t objects to OutputIterator out.
+ If the number of message parts of the incoming message exceeds n
+ then an exception will be thrown.
+
+ Returns: the number of messages received or nullopt (on EAGAIN).
+ Throws: if recv throws. Throws std::runtime_error if the number
+ of message parts exceeds n (exactly n messages will have been written
+ to out). Any exceptions thrown
+ by the out iterator will be propagated and the message
+ may have been only partially received with pending
+ message parts. It is adviced to close this socket in that event.
+*/
+template