├── doc ├── Samples-pubsub.md ├── Samples-pushpull.md ├── Samples-reqrep.md ├── Software-API-Reference.md ├── LICENSE.header ├── Samples.md └── Software.md ├── .hgignore ├── 3rdparty └── cppzmq │ ├── README │ ├── LICENSE │ └── zmq.hpp ├── nzmqt.pri ├── .gitignore ├── LICENSE.md ├── src ├── nzmqt │ └── nzmqt.cpp ├── app │ ├── main.cpp │ └── NzmqtApp.hpp ├── nzmqt_sharedlib.pro ├── nzmqt_staticlib.pro ├── nzmqt_test.pro ├── nzmqt_app.pro ├── pubsub │ ├── Subscriber.hpp │ └── Publisher.hpp ├── common │ └── SampleBase.hpp ├── reqrep │ ├── Replier.hpp │ └── Requester.hpp ├── pushpull │ ├── Worker.hpp │ ├── Sink.hpp │ └── Ventilator.hpp └── test │ └── nzmqt_test.cpp ├── include └── nzmqt │ ├── global.hpp │ ├── impl.hpp │ └── nzmqt.hpp ├── CHANGELOG.md └── README.md /doc/Samples-pubsub.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/Samples-pushpull.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/Samples-reqrep.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/Software-API-Reference.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | syntax: glob 2 | Debug 3 | Release 4 | **.user 5 | **.orig 6 | **.DS_Store 7 | 8 | -------------------------------------------------------------------------------- /3rdparty/cppzmq/README: -------------------------------------------------------------------------------- 1 | This is C++ binding for 0MQ 2 | 3 | The contribution policy is at: http://rfc.zeromq.org/spec:22 4 | -------------------------------------------------------------------------------- /nzmqt.pri: -------------------------------------------------------------------------------- 1 | # Include this file into your project to build and link 2 | # the nzmqt library into you application 3 | 4 | # This define will "move" nzmqt class method 5 | # implementations to nzmqt.cpp file. 6 | DEFINES += NZMQT_LIB 7 | 8 | SOURCES += \ 9 | $$PWD/src/nzmqt/nzmqt.cpp 10 | 11 | HEADERS += \ 12 | $$PWD/include/nzmqt/global.hpp \ 13 | $$PWD/include/nzmqt/nzmqt.hpp \ 14 | $$PWD/include/nzmqt/impl.hpp 15 | 16 | INCLUDEPATH += \ 17 | $$PWD/include \ 18 | $$PWD/3rdparty/cppzmq -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # In repository we don't need to have: 2 | # Compiled object files and libs 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | *.exe 13 | 14 | # Qt files 15 | moc_*.cpp 16 | qrc_*.cpp 17 | ui_*.h 18 | *.pro.user 19 | *.pro.user.* 20 | *-build-* 21 | Makefile 22 | 23 | # Debug and Release directories (created under Windows, not Linux) 24 | bin/ 25 | Debug/ 26 | Release/ 27 | 28 | # .log files (usually created by QtTest - thanks to VestniK) 29 | *.log 30 | 31 | # Windows-specific files 32 | Thumbs.db 33 | desktop.ini 34 | 35 | # Mac-specific things 36 | *.DS_Store 37 | 38 | # Editors temporary files 39 | *~ 40 | 41 | # Foreign VCS files. 42 | .hg* 43 | .bzr* 44 | .svn* 45 | 46 | # Project directories 47 | externals/ 48 | 49 | -------------------------------------------------------------------------------- /3rdparty/cppzmq/LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to 3 | deal in the Software without restriction, including without limitation the 4 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 5 | sell copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 16 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 17 | IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are 4 | permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of 7 | conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | of conditions and the following disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | The views and conclusions contained in the software and documentation are those of the 24 | authors and should not be interpreted as representing official policies, either expressed 25 | or implied, of Johann Duscher. 26 | -------------------------------------------------------------------------------- /doc/LICENSE.header: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2013 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | -------------------------------------------------------------------------------- /src/nzmqt/nzmqt.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #if defined(NZMQT_LIB) 28 | #include "nzmqt/nzmqt.hpp" 29 | #include "nzmqt/impl.hpp" 30 | #endif 31 | -------------------------------------------------------------------------------- /src/app/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #include 28 | 29 | #include "app/NzmqtApp.hpp" 30 | 31 | 32 | int main(int argc, char *argv[]) 33 | { 34 | nzmqt::samples::NzmqtApp nzmqtApp(argc, argv); 35 | 36 | return nzmqtApp.exec(); 37 | } 38 | -------------------------------------------------------------------------------- /include/nzmqt/global.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_GLOBAL_HPP 28 | #define NZMQT_GLOBAL_HPP 29 | 30 | #include 31 | 32 | #if defined(NZMQT_SHAREDLIB) 33 | # define NZMQT_API Q_DECL_EXPORT 34 | #else 35 | # define NZMQT_API Q_DECL_IMPORT 36 | #endif 37 | #if !defined(NZMQT_LIB) 38 | # undef NZMQT_API 39 | # define NZMQT_API 40 | #endif 41 | 42 | #endif // NZMQT_GLOBAL_HPP 43 | -------------------------------------------------------------------------------- /src/nzmqt_sharedlib.pro: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without modification, are 4 | # permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this list of 7 | # conditions and the following disclaimer. 8 | # 9 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | # of conditions and the following disclaimer in the documentation and/or other materials 11 | # provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | # 23 | # The views and conclusions contained in the software and documentation are those of the 24 | # authors and should not be interpreted as representing official policies, either expressed 25 | # or implied, of Johann Duscher. 26 | 27 | 28 | QT -= gui 29 | 30 | TARGET = nzmqt 31 | VERSION = 3.2.1 32 | DESTDIR = $$_PRO_FILE_PWD_/../bin 33 | TEMPLATE = lib 34 | 35 | CONFIG += debug_and_release 36 | CONFIG(debug, debug|release) { 37 | TARGET = $$join(TARGET,,,d) 38 | } 39 | 40 | DEFINES += \ 41 | NZMQT_LIB \ 42 | NZMQT_SHAREDLIB 43 | 44 | SOURCES += \ 45 | nzmqt/nzmqt.cpp 46 | 47 | HEADERS += \ 48 | ../include/nzmqt/global.hpp \ 49 | ../include/nzmqt/nzmqt.hpp \ 50 | ../include/nzmqt/impl.hpp 51 | 52 | LIBS += -lzmq 53 | 54 | INCLUDEPATH += \ 55 | ../include \ 56 | ../3rdparty/cppzmq \ 57 | $(QTDIR)/include \ 58 | /opt/local/include 59 | 60 | QMAKE_LIBDIR += \ 61 | /opt/local/lib 62 | 63 | OTHER_FILES += \ 64 | ../README.md \ 65 | ../LICENSE.header \ 66 | ../CHANGELOG.md \ 67 | ../LICENSE.md 68 | -------------------------------------------------------------------------------- /src/nzmqt_staticlib.pro: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without modification, are 4 | # permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this list of 7 | # conditions and the following disclaimer. 8 | # 9 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | # of conditions and the following disclaimer in the documentation and/or other materials 11 | # provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | # 23 | # The views and conclusions contained in the software and documentation are those of the 24 | # authors and should not be interpreted as representing official policies, either expressed 25 | # or implied, of Johann Duscher. 26 | 27 | 28 | QT -= gui 29 | 30 | TARGET = nzmqt 31 | VERSION = 3.2.1 32 | DESTDIR = $$_PRO_FILE_PWD_/../bin 33 | TEMPLATE = lib 34 | CONFIG += staticlib 35 | 36 | CONFIG += debug_and_release 37 | CONFIG(debug, debug|release) { 38 | TARGET = $$join(TARGET,,,d) 39 | } 40 | 41 | DEFINES += \ 42 | NZMQT_LIB 43 | 44 | SOURCES += \ 45 | nzmqt/nzmqt.cpp 46 | 47 | HEADERS += \ 48 | ../include/nzmqt/global.hpp \ 49 | ../include/nzmqt/nzmqt.hpp \ 50 | ../include/nzmqt/impl.hpp 51 | 52 | LIBS += -lzmq 53 | 54 | INCLUDEPATH += \ 55 | ../include \ 56 | ../3rdparty/cppzmq \ 57 | $(QTDIR)/include \ 58 | /opt/local/include 59 | 60 | QMAKE_LIBDIR += \ 61 | /opt/local/lib 62 | 63 | OTHER_FILES += \ 64 | ../README.md \ 65 | ../LICENSE.header \ 66 | ../CHANGELOG.md \ 67 | ../LICENSE.md 68 | -------------------------------------------------------------------------------- /src/nzmqt_test.pro: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without modification, are 4 | # permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this list of 7 | # conditions and the following disclaimer. 8 | # 9 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | # of conditions and the following disclaimer in the documentation and/or other materials 11 | # provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | # 23 | # The views and conclusions contained in the software and documentation are those of the 24 | # authors and should not be interpreted as representing official policies, either expressed 25 | # or implied, of Johann Duscher. 26 | 27 | QT += testlib 28 | 29 | QT -= gui 30 | 31 | TARGET = nzmqt_test 32 | VERSION = 3.2.1 33 | DESTDIR = $$_PRO_FILE_PWD_/../bin 34 | CONFIG += console 35 | CONFIG -= app_bundle 36 | 37 | TEMPLATE = app 38 | 39 | DEFINES += \ 40 | # NZMQT_LIB \ 41 | SRCDIR=\\\"$$PWD/\\\" 42 | 43 | SOURCES += \ 44 | test/nzmqt_test.cpp 45 | 46 | HEADERS += \ 47 | ../include/nzmqt/nzmqt.hpp \ 48 | common/SampleBase.hpp \ 49 | pubsub/Publisher.hpp \ 50 | pubsub/Subscriber.hpp \ 51 | pushpull/Sink.hpp \ 52 | pushpull/Ventilator.hpp \ 53 | pushpull/Worker.hpp \ 54 | reqrep/Replier.hpp \ 55 | reqrep/Requester.hpp 56 | 57 | LIBS += -lzmq 58 | 59 | INCLUDEPATH += \ 60 | ../include \ 61 | ../3rdparty/cppzmq \ 62 | $(QTDIR)/include \ 63 | /opt/local/include 64 | 65 | QMAKE_LIBDIR += \ 66 | /opt/local/lib 67 | 68 | OTHER_FILES += \ 69 | ../README.md \ 70 | ../LICENSE.header \ 71 | ../CHANGELOG.md \ 72 | ../LICENSE.md 73 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Releases for ZMQ 3.x 2 | 3 | ## Release 3.2.1 (TO BE RELEASED) 4 | 5 | ### API Changes 6 | 7 | * Convert ZMQSocket::sendMessage(...) methods to slots. 8 | 9 | ## Release 3.2.0 10 | 11 | * Improved documentation. 12 | * Emit poll errors as signals (instead of ignoring them). 13 | * Fixed gh-6; Exception thrown during poll. 14 | * Fixed gh-7: Fix occasional incorrect result from hasMoreMessageParts. 15 | * Fixed gh-8: Unsafe disconnect in PollingZMQContext::unregisterSocket(QObject\* socket_). 16 | * Fixed gh-12: warnings with -Wall (and compilation broken if -Werror). 17 | * Feature gh-13: Support type-safe Qt 5 singals & slots connections. 18 | * Feature gh-14: Additionally allow to link nzmqt 3.2.0 as static or shared lib. 19 | * Task gh-16: Implement unit tests. 20 | 21 | ### API Changes 22 | 23 | * Method 'ZMQContext::createSocket()' does not implicitly set the 'ZMQContext' instance as parent of the created instance anymore. So if you don't pass a pointer to a parent QObject (NULL pointer) the socket instance won't have a parent set. Code doing this will **leak memory** now! Please make sure you delete the socket instance yourself in such cases. 24 | * Method 'ZMQSocket::close()' now is a slot. 25 | 26 | # Releases for ZMQ 2.x 27 | 28 | ## Release 2.2.1 29 | 30 | * Improved documentation. 31 | * Emit poll errors as signals (instead of ignoring them). 32 | * Fixed gh-8: Unsafe disconnect in PollingZMQContext::unregisterSocket(QObject\* socket_). 33 | * Fixed gh-12: warnings with -Wall (and compilation broken if -Werror). 34 | * Feature gh-13: Support type-safe Qt 5 singals & slots connections. 35 | * Feature gh-15: Additionally allow to link nzmqt 2.2.1 as static or shared lib. 36 | * Task gh-16: Implement unit tests. 37 | 38 | ### API Changes 39 | 40 | * Method 'ZMQContext::createSocket()' does not implicitly set the 'ZMQContext' instance as parent of the created instance anymore. So if you don't pass a pointer to a parent QObject (NULL pointer) the socket instance won't have a parent set. Code doing this will **leak memory** now! Please make sure you delete the socket instance yourself in such cases. 41 | * Method 'ZMQSocket::close()' now is a slot. 42 | 43 | 44 | ## Release 2.2.0 45 | 46 | * fixed gh-5: Change PollingZMQContext::poll to empty the socket queue rather than poll once per interval. 47 | 48 | 49 | # Older Releases 50 | 51 | ## Release 0.7 52 | 53 | * Introduced enumeration types for several ZMQ constants for type-safety. 54 | * Added a new polling based implementation that works for all ZMQ communication protocols. 55 | * Dropped support for REQ-REP protocol for old 'QSocketNotifier' based implementation. 56 | * Added some more convenience methods to 'ZMQSocket' class. 57 | * Old and new socket implementations now emit a signal with a received message as parameter. 58 | 59 | -------------------------------------------------------------------------------- /src/nzmqt_app.pro: -------------------------------------------------------------------------------- 1 | # Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | # 3 | # Redistribution and use in source and binary forms, with or without modification, are 4 | # permitted provided that the following conditions are met: 5 | # 6 | # 1. Redistributions of source code must retain the above copyright notice, this list of 7 | # conditions and the following disclaimer. 8 | # 9 | # 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | # of conditions and the following disclaimer in the documentation and/or other materials 11 | # provided with the distribution. 12 | # 13 | # THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | # FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | # 23 | # The views and conclusions contained in the software and documentation are those of the 24 | # authors and should not be interpreted as representing official policies, either expressed 25 | # or implied, of Johann Duscher. 26 | 27 | 28 | QT += core 29 | 30 | QT -= gui 31 | 32 | TARGET = nzmqt_app 33 | VERSION = 3.2.1 34 | DESTDIR = $$_PRO_FILE_PWD_/../bin 35 | CONFIG += console 36 | CONFIG -= app_bundle 37 | 38 | TEMPLATE = app 39 | 40 | DEFINES += \ 41 | # Uncomment this line for nzmqt "Include and Source File" compilation option. 42 | # NZMQT_LIB 43 | 44 | SOURCES += \ 45 | # Uncomment this line for nzmqt "Include and Source File" compilation option. 46 | # nzmqt/nzmqt.cpp \ 47 | app/main.cpp 48 | 49 | HEADERS += \ 50 | ../include/nzmqt/nzmqt.hpp \ 51 | common/SampleBase.hpp \ 52 | pubsub/Subscriber.hpp \ 53 | pubsub/Publisher.hpp \ 54 | pushpull/Sink.hpp \ 55 | pushpull/Worker.hpp \ 56 | pushpull/Ventilator.hpp \ 57 | reqrep/Requester.hpp \ 58 | reqrep/Replier.hpp \ 59 | app/NzmqtApp.hpp 60 | 61 | LIBS += -lzmq 62 | 63 | INCLUDEPATH += \ 64 | ../include \ 65 | ../3rdparty/cppzmq \ 66 | $(QTDIR)/include \ 67 | /opt/local/include 68 | 69 | QMAKE_LIBDIR += \ 70 | /opt/local/lib 71 | 72 | OTHER_FILES += \ 73 | ../README.md \ 74 | ../LICENSE.header \ 75 | ../CHANGELOG.md \ 76 | ../LICENSE.md 77 | -------------------------------------------------------------------------------- /doc/Samples.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | There are some samples showing *PUB-SUB*, *REQ-REP* and *PUSH-PULL* protocol with multi-part messages in action. As nzmqt uses C++ exceptions for error handling you will need to catch them by overriding ``QCoreApplication::notify()`` method. The included samples will show you how this can be done. 5 | 6 | Running samples 7 | --------------- 8 | 9 | All samples are compiled into one executable called ``nzmqt_app``. You can try them out by providing appropriate command line parameters. If you don't provide any, or if you provide ``-h`` or ``--help`` option, a list of all examples together with required parameters will be shown: 10 | 11 | ``` 12 | USAGE: ./nzmqt_app [-h|--help] -- Show this help message. 13 | 14 | USAGE: ./nzmqt_app pubsub-publisher
-- Start PUB server. 15 | ./nzmqt_app pubsub-subscriber
-- Start SUB client. 16 | 17 | USAGE: ./nzmqt_app reqrep-replier
-- Start REQ server. 18 | ./nzmqt_app reqrep-requester
-- Start REP client. 19 | 20 | USAGE: ./nzmqt_app pushpull-ventilator -- Start ventilator. 21 | ./nzmqt_app pushpull-worker -- Start a worker. 22 | ./nzmqt_app pushpull-sink -- Start sink. 23 | 24 | Publish-Subscribe Sample: 25 | * Publisher: ./nzmqt_app pubsub-publisher tcp://127.0.0.1:1234 ping 26 | * Subscriber: ./nzmqt_app pubsub-subscriber tcp://127.0.0.1:1234 ping 27 | 28 | Request-Reply Sample: 29 | * Replier: ./nzmqt_app reqrep-replier tcp://127.0.0.1:1234 World 30 | * Requester: ./nzmqt_app reqrep-requester tcp://127.0.0.1:1234 Hello 31 | 32 | Push-Pull Sample: 33 | * Ventilator: ./nzmqt_app pushpull-ventilator tcp://127.0.0.1:5557 tcp://127.0.0.1:5558 100 34 | * Worker 1..n: ./nzmqt_app pushpull-worker tcp://127.0.0.1:5557 tcp://127.0.0.1:5558 35 | * Sink: ./nzmqt_app pushpull-sink tcp://127.0.0.1:5558 36 | ``` 37 | 38 | As can be seen, this output also provides examples showing how to run them with concrete command line parameters. You can directly copy and paste them to a shell prompt and run them without changing anything (assumed the given ports are free). 39 | 40 | Documentation 41 | ------------- 42 | 43 | Sample-specific information can be found here: 44 | * [pubsub][]: Demonstrates how to implement PUB-SUB protocol. 45 | * [pushpull][]: Demonstrates how to implement PUSH-PULL protocol. 46 | * [reqrep][]: Demonstrates how to implement REQ-REP protocol. 47 | 48 | 49 | [pubsub]: Samples-pubsub.md "PUB-SUB protocol example" 50 | [pushpull]: Samples-pushpull.md "PUSH-PULL protocol example" 51 | [reqrep]: Samples-reqrep.md "REQ-REP protocol example" 52 | -------------------------------------------------------------------------------- /src/pubsub/Subscriber.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_PUBSUBCLIENT_H 28 | #define NZMQT_PUBSUBCLIENT_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | 38 | namespace nzmqt 39 | { 40 | 41 | namespace samples 42 | { 43 | 44 | namespace pubsub 45 | { 46 | 47 | class Subscriber : public SampleBase 48 | { 49 | Q_OBJECT 50 | typedef SampleBase super; 51 | 52 | public: 53 | explicit Subscriber(ZMQContext& context, const QString& address, const QString& topic, QObject *parent = 0) 54 | : super(parent) 55 | , address_(address), topic_(topic) 56 | , socket_(0) 57 | { 58 | socket_ = context.createSocket(ZMQSocket::TYP_SUB, this); 59 | socket_->setObjectName("Subscriber.Socket.socket(SUB)"); 60 | connect(socket_, SIGNAL(messageReceived(const QList&)), SLOT(messageReceived(const QList&))); 61 | } 62 | 63 | signals: 64 | void pingReceived(const QList& message); 65 | 66 | protected: 67 | void startImpl() 68 | { 69 | socket_->subscribeTo(topic_); 70 | socket_->connectTo(address_); 71 | } 72 | 73 | protected slots: 74 | void messageReceived(const QList& message) 75 | { 76 | qDebug() << "Subscriber> " << message; 77 | emit pingReceived(message); 78 | } 79 | 80 | private: 81 | QString address_; 82 | QString topic_; 83 | 84 | ZMQSocket* socket_; 85 | }; 86 | 87 | } 88 | 89 | } 90 | 91 | } 92 | 93 | #endif // NZMQT_PUBSUBCLIENT_H 94 | -------------------------------------------------------------------------------- /src/common/SampleBase.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_SAMPLEBASE_H 28 | #define NZMQT_SAMPLEBASE_H 29 | 30 | #include "nzmqt/nzmqt.hpp" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | 37 | namespace nzmqt 38 | { 39 | 40 | namespace samples 41 | { 42 | 43 | class SampleBase : public QObject 44 | { 45 | Q_OBJECT 46 | typedef QObject super; 47 | 48 | signals: 49 | void finished(); 50 | void failure(const QString& what); 51 | 52 | public slots: 53 | void start(); 54 | void stop(); 55 | 56 | protected: 57 | SampleBase(QObject* parent); 58 | 59 | virtual void startImpl() = 0; 60 | 61 | static void sleep(unsigned long msecs); 62 | 63 | private: 64 | class ThreadTools : private QThread 65 | { 66 | public: 67 | using QThread::msleep; 68 | 69 | private: 70 | ThreadTools() {} 71 | }; 72 | }; 73 | 74 | inline SampleBase::SampleBase(QObject* parent) 75 | : super(parent) 76 | { 77 | } 78 | 79 | inline void SampleBase::start() 80 | { 81 | try 82 | { 83 | startImpl(); 84 | } 85 | catch (const nzmqt::ZMQException& ex) 86 | { 87 | qWarning() << Q_FUNC_INFO << "Exception:" << ex.what(); 88 | emit failure(ex.what()); 89 | emit finished(); 90 | } 91 | } 92 | 93 | inline void SampleBase::stop() 94 | { 95 | emit finished(); 96 | } 97 | 98 | inline void SampleBase::sleep(unsigned long msecs) 99 | { 100 | ThreadTools::msleep(msecs); 101 | } 102 | 103 | } 104 | 105 | } 106 | 107 | #endif // NZMQT_SAMPLEBASE_H 108 | -------------------------------------------------------------------------------- /src/pubsub/Publisher.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_PUBSUBSERVER_H 28 | #define NZMQT_PUBSUBSERVER_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | namespace nzmqt 41 | { 42 | 43 | namespace samples 44 | { 45 | 46 | namespace pubsub 47 | { 48 | 49 | class Publisher : public SampleBase 50 | { 51 | Q_OBJECT 52 | typedef SampleBase super; 53 | 54 | public: 55 | explicit Publisher(ZMQContext& context, const QString& address, const QString& topic, QObject* parent = 0) 56 | : super(parent) 57 | , address_(address), topic_(topic) 58 | , socket_(0) 59 | { 60 | socket_ = context.createSocket(ZMQSocket::TYP_PUB, this); 61 | socket_->setObjectName("Publisher.Socket.socket(PUB)"); 62 | } 63 | 64 | signals: 65 | void pingSent(const QList& message); 66 | 67 | protected: 68 | void startImpl() 69 | { 70 | socket_->bindTo(address_); 71 | 72 | QTimer::singleShot(1000, this, SLOT(sendPing())); 73 | } 74 | 75 | protected slots: 76 | void sendPing() 77 | { 78 | static quint64 counter = 0; 79 | 80 | QList< QByteArray > msg; 81 | msg += topic_.toLocal8Bit(); 82 | msg += QString("MSG[%1: %2]").arg(++counter).arg(QDateTime::currentDateTime().toLocalTime().toString(Qt::ISODate)).toLocal8Bit(); 83 | socket_->sendMessage(msg); 84 | qDebug() << "Publisher> " << msg; 85 | emit pingSent(msg); 86 | 87 | QTimer::singleShot(1000, this, SLOT(sendPing())); 88 | } 89 | 90 | private: 91 | QString address_; 92 | QString topic_; 93 | 94 | ZMQSocket* socket_; 95 | }; 96 | 97 | } 98 | 99 | } 100 | 101 | } 102 | 103 | #endif // NZMQT_PUBSUBSERVER_H 104 | -------------------------------------------------------------------------------- /src/reqrep/Replier.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_REQREPSERVER_H 28 | #define NZMQT_REQREPSERVER_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | namespace nzmqt 40 | { 41 | 42 | namespace samples 43 | { 44 | 45 | namespace reqrep 46 | { 47 | 48 | class Replier : public SampleBase 49 | { 50 | Q_OBJECT 51 | typedef SampleBase super; 52 | 53 | public: 54 | explicit Replier(ZMQContext& context, const QString& address, const QString& replyMsg, QObject* parent = 0) 55 | : super(parent) 56 | , address_(address), replyMsg_(replyMsg) 57 | , socket_(0) 58 | { 59 | socket_ = context.createSocket(ZMQSocket::TYP_REP, this); 60 | socket_->setObjectName("Replier.Socket.socket(REP)"); 61 | connect(socket_, SIGNAL(messageReceived(const QList&)), SLOT(receiveRequest(const QList&))); 62 | } 63 | 64 | signals: 65 | void requestReceived(const QList& request); 66 | void replySent(const QList& request); 67 | 68 | protected: 69 | void startImpl() 70 | { 71 | socket_->bindTo(address_); 72 | } 73 | 74 | protected slots: 75 | void receiveRequest(const QList& request) 76 | { 77 | static quint64 counter = 0; 78 | 79 | qDebug() << "Replier::requestReceived> " << request; 80 | emit requestReceived(request); 81 | 82 | QList reply; 83 | reply += QString("REPLY[%1: %2]").arg(++counter).arg(QDateTime::currentDateTime().toString(Qt::ISODate)).toLocal8Bit(); 84 | reply += replyMsg_.toLocal8Bit(); 85 | reply += request; // We also append original request. 86 | qDebug() << "Replier::sendReply> " << reply; 87 | socket_->sendMessage(reply); 88 | emit replySent(reply); 89 | } 90 | 91 | private: 92 | QString address_; 93 | QString replyMsg_; 94 | 95 | ZMQSocket* socket_; 96 | }; 97 | 98 | } 99 | 100 | } 101 | 102 | } 103 | 104 | #endif // NZMQT_REQREPSERVER_H 105 | -------------------------------------------------------------------------------- /src/pushpull/Worker.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_PUSHPULLWORKER_H 28 | #define NZMQT_PUSHPULLWORKER_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | 38 | namespace nzmqt 39 | { 40 | 41 | namespace samples 42 | { 43 | 44 | namespace pushpull 45 | { 46 | 47 | class Worker : public SampleBase 48 | { 49 | Q_OBJECT 50 | typedef SampleBase super; 51 | 52 | public: 53 | explicit Worker(ZMQContext& context, const QString& ventilatorAddress, const QString& sinkAddress, QObject *parent = 0) 54 | : super(parent) 55 | , ventilatorAddress_(ventilatorAddress), sinkAddress_(sinkAddress) 56 | , ventilator_(0), sink_(0) 57 | { 58 | sink_ = context.createSocket(ZMQSocket::TYP_PUSH, this); 59 | sink_->setObjectName("Worker.Socket.sink(PUSH)"); 60 | 61 | ventilator_ = context.createSocket(ZMQSocket::TYP_PULL, this); 62 | ventilator_->setObjectName("Worker.Socket.ventilator(PULL)"); 63 | connect(ventilator_, SIGNAL(messageReceived(const QList&)), SLOT(receiveWorkItem(const QList&))); 64 | } 65 | 66 | signals: 67 | void workItemReceived(quint32 workload); 68 | void workItemResultSent(); 69 | 70 | protected: 71 | void startImpl() 72 | { 73 | sink_->connectTo(sinkAddress_); 74 | ventilator_->connectTo(ventilatorAddress_); 75 | } 76 | 77 | protected slots: 78 | void receiveWorkItem(const QList& message) 79 | { 80 | quint32 work = QString(message[0]).toUInt(); 81 | emit workItemReceived(work); 82 | 83 | // Do the work ;-) 84 | qDebug() << "snore" << work << "msec"; 85 | sleep(work); 86 | 87 | // Send results to sink. 88 | sink_->sendMessage(""); 89 | emit workItemResultSent(); 90 | } 91 | 92 | private: 93 | QString ventilatorAddress_; 94 | QString sinkAddress_; 95 | 96 | ZMQSocket* ventilator_; 97 | ZMQSocket* sink_; 98 | }; 99 | 100 | } 101 | 102 | } 103 | 104 | } 105 | 106 | #endif // NZMQT_PUSHPULLWORKER_H 107 | -------------------------------------------------------------------------------- /src/reqrep/Requester.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_REQREPCLIENT_H 28 | #define NZMQT_REQREPCLIENT_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | namespace nzmqt 41 | { 42 | 43 | namespace samples 44 | { 45 | 46 | namespace reqrep 47 | { 48 | 49 | class Requester : public SampleBase 50 | { 51 | Q_OBJECT 52 | typedef SampleBase super; 53 | 54 | public: 55 | explicit Requester(ZMQContext& context, const QString& address, const QString& requestMsg, QObject *parent = 0) 56 | : super(parent) 57 | , address_(address), requestMsg_(requestMsg) 58 | , socket_(0) 59 | { 60 | socket_ = context.createSocket(ZMQSocket::TYP_REQ, this); 61 | socket_->setObjectName("Requester.Socket.socket(REQ)"); 62 | connect(socket_, SIGNAL(messageReceived(const QList&)), SLOT(receiveReply(const QList&))); 63 | } 64 | 65 | signals: 66 | void requestSent(const QList& request); 67 | void replyReceived(const QList& reply); 68 | 69 | protected: 70 | void startImpl() 71 | { 72 | socket_->connectTo(address_); 73 | 74 | QTimer::singleShot(1000, this, SLOT(sendRequest())); 75 | } 76 | 77 | protected slots: 78 | void sendRequest() 79 | { 80 | static quint64 counter = 0; 81 | 82 | QList request; 83 | request += QString("REQUEST[%1: %2]").arg(++counter).arg(QDateTime::currentDateTime().toString(Qt::ISODate)).toLocal8Bit(); 84 | request += requestMsg_.toLocal8Bit(); 85 | qDebug() << "Requester::sendRequest> " << request; 86 | socket_->sendMessage(request); 87 | emit requestSent(request); 88 | } 89 | 90 | void receiveReply(const QList& reply) 91 | { 92 | qDebug() << "Requester::replyReceived> " << reply; 93 | emit replyReceived(reply); 94 | 95 | // Start timer again in order to trigger the next sendRequest() call. 96 | QTimer::singleShot(1000, this, SLOT(sendRequest())); 97 | } 98 | 99 | private: 100 | QString address_; 101 | QString requestMsg_; 102 | 103 | ZMQSocket* socket_; 104 | }; 105 | 106 | } 107 | 108 | } 109 | 110 | } 111 | 112 | #endif // NZMQT_REQREPCLIENT_H 113 | -------------------------------------------------------------------------------- /src/pushpull/Sink.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_PUSHPULLSINK_H 28 | #define NZMQT_PUSHPULLSINK_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | namespace nzmqt 40 | { 41 | 42 | namespace samples 43 | { 44 | 45 | namespace pushpull 46 | { 47 | 48 | class Sink : public SampleBase 49 | { 50 | Q_OBJECT 51 | typedef SampleBase super; 52 | 53 | public: 54 | explicit Sink(ZMQContext& context, const QString& sinkAddress, QObject *parent = 0) 55 | : super(parent) 56 | , sinkAddress_(sinkAddress) 57 | , sink_(0) 58 | , numberOfWorkItems_(-1) 59 | { 60 | sink_ = context.createSocket(ZMQSocket::TYP_PULL, this); 61 | sink_->setObjectName("Sink.Socket.sink(PULL)"); 62 | connect(sink_, SIGNAL(messageReceived(const QList&)), SLOT(batchEvent(const QList&))); 63 | } 64 | 65 | signals: 66 | void batchStarted(int numberOfWorkItems); 67 | void workItemResultReceived(); 68 | void batchCompleted(); 69 | 70 | protected: 71 | void startImpl() 72 | { 73 | sink_->bindTo(sinkAddress_); 74 | } 75 | 76 | protected slots: 77 | void batchEvent(const QList& message) 78 | { 79 | if (numberOfWorkItems_ < 0) 80 | { 81 | // 'message' is a batch start message. 82 | numberOfWorkItems_ = message[0].toUInt(); 83 | qDebug() << "Batch started for >" << numberOfWorkItems_ << "< work items."; 84 | stopWatch_.start(); 85 | batchStarted(numberOfWorkItems_); 86 | 87 | if (numberOfWorkItems_) 88 | return; 89 | } 90 | 91 | if (numberOfWorkItems_ > 0) 92 | { 93 | if (numberOfWorkItems_ % 10 == 0) 94 | qDebug() << numberOfWorkItems_; 95 | else 96 | qDebug() << "."; 97 | 98 | --numberOfWorkItems_; 99 | emit workItemResultReceived(); 100 | } 101 | 102 | if (!numberOfWorkItems_) 103 | { 104 | int msec = stopWatch_.elapsed(); 105 | qDebug() << "FINISHED all task in " << msec << "msec"; 106 | numberOfWorkItems_ = -1; 107 | emit batchCompleted(); 108 | } 109 | } 110 | 111 | private: 112 | QString sinkAddress_; 113 | ZMQSocket* sink_; 114 | 115 | int numberOfWorkItems_; 116 | QTime stopWatch_; 117 | }; 118 | 119 | } 120 | 121 | } 122 | 123 | } 124 | 125 | #endif // NZMQT_PUSHPULLSINK_H 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nzmqt - A lightweight C++ Qt binding for 0MQ 2 | ============================================ 3 | 4 | [nzmqt][] is a lightweight C++ [Qt][] binding for [0MQ / zeromq][zeromq]. The primary goal of this project is to provide a Qt-ish interface to 0MQ intelligent transport layer library and to integrate it into Qt's event loop seamlessly. 5 | 6 | Why use nzmqt 7 | ------------- 8 | 9 | If you use Qt framework for platform independent software development and you want to use 0MQ transport layer library you are facing the problem to integrate message queue into Qt's event system somehow. There is already a C++ Qt binding [zeromqt][] which approaches this problem by integrating 0MQ into the Qt's event loop, mapping 0MQ message events onto Qt signals. Furthermore, it provides a Qt-like API which allows to represent messages as QByteArray instances. This is a nice idea! However, there are some drawbacks with the project's code and implementation, which is why I decided to start this project. 10 | 11 | nzmqt is a re-implementation of the approach taken by [zeromqt][]. I took the original implementation as a source of information, but I've done a completely new implementation. I could have built upon the exiting code by just forking it, but I wanted to be sure I can use the code in my projects without problems, because until now zeromqt's author hasn't officially released his work under a certain (open source) license. Consequently, nzmqt is released to the public under the simplified BSD license. So you can use the code in your (commercial) projects without any fear. Another short coming with zeromqt is that it works fine for 0MQ non-multi-part messages, but it doesn't support multi-part messages yet. Also, it duplicates a lot of code of [0MQ's standard C++ binding][cppzmq] and, as a result, cannot profit from code improvements and bugfixes without copying or incorporating them into zeromqt's implementation. So in contrast to the original implementation, nzmqt reuses as much code of 0MQ's original C++ binding as possible by using inheritance. Additionally, several other things have been changed from an API user's perspective. In summary, nzmqt contains the following changes and improvements compared to zeromqt: 12 | 13 | * The implementation is a complete re-write in the sense that it doesn't duplicate code of 0MQ's official C++ binding anymore. Instead, it builds upon existing code through inheritance and, hence, will likely benefit from future bugfixes and enhancements targeted at 0MQ's C++ binding. 14 | * All classes are placed into a separate namespace 'nzmqt'. 15 | * 0MQ's multi-part messages are supported. 16 | * The initial support for using Qt's way of handling errors using error codes has been dropped. Instead, this code only throws exceptions originally thrown by 0MQ's official C++ API. 17 | * As with 0MQ's C++ binding all classes are contained within a singe header file which makes integrating this Qt binding very easy. 18 | * There is no 'ZmqContext' singleton anymore. Instead you can create your own instance of a concrete subclass of 'ZMQContext' yourself. 19 | * The socket class 'ZMQSocket' now also inherits from QObject, so you can add it as a child to any QObject parent as you know it from Qt. 20 | * The code is officially licensed under the (commercial-friendly) simplified BSD license. 21 | * Not only PUB-SUB, but also REQ-REP and PUSH-PULL are supported. 22 | 23 | More Information 24 | ---------------- 25 | 26 | * [changelog][]: What's new? Read about changes introduced with each release. 27 | * [nzmqt issue tracker][]: Find out about the current project status, or issue bug reports and feature requests. 28 | * [software overview][]: How to get started, API reference documentation, etc. 29 | * [samples overview][]: Explanation of the provided examples. 30 | 31 | 32 | [cppzmq]: https://github.com/zeromq/cppzmq "C++ binding for 0MQ on GitHub" 33 | [nzmqt]: https://github.com/jonnydee/nzmqt "nzmqt project on GitHub" 34 | [nzmqt issue tracker]: https://github.com/jonnydee/nzmqt/issues "nzmqt issue tracker on GitHub" 35 | [Qt]: http://qt-project.org/ "Qt project homepage" 36 | [zeromq]: http://www.zeromq.org/ "0MQ project homepage" 37 | [zeromqt]: https://github.com/wttw/zeromqt "zeromqt project on GitHub" 38 | 39 | [changelog]: CHANGELOG.md "nzmqt software changelog" 40 | [samples overview]: doc/Samples.md "nzmqt samples overview" 41 | [software overview]: doc/Software.md "nzmqt software overview" 42 | -------------------------------------------------------------------------------- /src/pushpull/Ventilator.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_PUSHPULLVENTILATOR_H 28 | #define NZMQT_PUSHPULLVENTILATOR_H 29 | 30 | #include "common/SampleBase.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | 40 | namespace nzmqt 41 | { 42 | 43 | namespace samples 44 | { 45 | 46 | namespace pushpull 47 | { 48 | 49 | class Ventilator : public SampleBase 50 | { 51 | Q_OBJECT 52 | typedef SampleBase super; 53 | 54 | public: 55 | explicit Ventilator(ZMQContext& context, const QString& ventilatorAddress, const QString& sinkAddress, quint32 numberOfWorkItems, QObject* parent = 0) 56 | : super(parent) 57 | , ventilatorAddress_(ventilatorAddress), sinkAddress_(sinkAddress), numberOfWorkItems_(numberOfWorkItems) 58 | , ventilator_(0), sink_(0) 59 | { 60 | ventilator_ = context.createSocket(ZMQSocket::TYP_PUSH, this); 61 | ventilator_->setObjectName("Ventilator.Socket.ventilator(PUSH)"); 62 | 63 | sink_ = context.createSocket(ZMQSocket::TYP_PUSH, this); 64 | sink_->setObjectName("Ventilator.Socket.sink(PUSH)"); 65 | } 66 | 67 | int numberOfWorkItems() const 68 | { 69 | return numberOfWorkItems_; 70 | } 71 | 72 | int maxWorkLoad() const 73 | { 74 | return 100; 75 | } 76 | 77 | signals: 78 | void batchStarted(int); 79 | void workItemSent(quint32 workload); 80 | 81 | protected: 82 | void startImpl() 83 | { 84 | ventilator_->bindTo(ventilatorAddress_); 85 | sink_->connectTo(sinkAddress_); 86 | 87 | // Start batch after some period of time needed to setup workers. 88 | QTimer::singleShot(1000, this, SLOT(runBatch())); 89 | } 90 | 91 | protected slots: 92 | void runBatch() 93 | { 94 | // The first message tells the sink how much work it needs to do 95 | // and at the same time signals start of batch. 96 | 97 | sink_->sendMessage(QString::number(numberOfWorkItems()).toLocal8Bit()); 98 | emit batchStarted(numberOfWorkItems()); 99 | 100 | // Initialize random number generator. 101 | 102 | qsrand(QTime::currentTime().msec()); 103 | 104 | // Send work items. 105 | 106 | int totalExpectedCost = 0; // Total expected cost in msecs 107 | 108 | for (int workItem = 0; workItem < numberOfWorkItems(); workItem++) { 109 | // Random workload from 1 to 100msecs 110 | int workload = qrand() % maxWorkLoad() + 1; 111 | // Update toal cost. 112 | totalExpectedCost += workload; 113 | // Push workload. 114 | ventilator_->sendMessage(QString::number(workload).toLocal8Bit()); 115 | emit workItemSent(workload); 116 | } 117 | 118 | qDebug() << "Total expected cost: " << totalExpectedCost << " msec"; 119 | 120 | QTimer::singleShot(0, this, SLOT(stop())); 121 | } 122 | 123 | private: 124 | QString ventilatorAddress_; 125 | QString sinkAddress_; 126 | quint32 numberOfWorkItems_; 127 | 128 | ZMQSocket* ventilator_; 129 | ZMQSocket* sink_; 130 | }; 131 | 132 | } 133 | 134 | } 135 | 136 | } 137 | 138 | #endif // NZMQT_PUSHPULLVENTILATOR_H 139 | -------------------------------------------------------------------------------- /doc/Software.md: -------------------------------------------------------------------------------- 1 | Overview 2 | ======== 3 | 4 | Getting Started 5 | --------------- 6 | 7 | ### Download nzmqt 8 | 9 | First of all, decide which version of nzmqt you will need. Your choice will depend on the version of 0MQ you are going to use. In order to make this choice easier a new versioning scheme for nzmqt has been introduced. nzmqt version numbers indicate compatibility with 0MQ version <0MQ-major>.<0MQ-minor>.<0MQ-bugfix> by adopting the major and minor part. For instance, nzmqt releases compatible with all 0MQ versions 2.2.x will have a corresponding version number 2.2.y, and an nzmqt version number 3.2.y indicates compatibility with all 0MQ versions 3.2.x. Note that there is no relation between nzmqt's and 0MQ's bugfix versions as bugfixes do not introduce incompatible API changes. Also note that release 0.7 is the last release NOT conforming to this scheme. 10 | 11 | Once you know which version you need you can either clone the complete nzmqt code repository or you can directly download the sources of a release as a ZIP file from GitHub. 12 | 13 | ### Resolve dependencies 14 | 15 | As nzmqt is a Qt binding for 0MQ there is obviously a dependency to these two libraries. Since 0MQ version 3 the C++ binding for 0MQ is not part of the core anymore. It has been externalized to a [separate Git repository][cppzmq]. 16 | 17 | So here is the complete list of dependencies: 18 | 19 | * [Qt 4.8.x][] (or newer): You will need to download and install it yourself. 20 | * [0MQ 3.2.x][zeromq 3.2.x]: You will need to download and install it yourself. 21 | * [C++ binding for 0MQ][cppzmq]: A script delivered with nzmqt will download the source code and appropriate version itself. 22 | 23 | ### Setup nzmqt project 24 | 25 | As already mentioned in the previous section nzmqt comes with a short script that will make sure a compatible version of 0MQ's C++ binding is available by downloading the correct version. In fact, the corresponding GitHub project repository is referenced as a Git submodule. So the script actually initializes the submodule. Furthermore, it will copy the C++ binding's include file to ``/externals/include`` directory in the project root. 26 | 27 | ***Execute setup script*** 28 | 29 | In a console type: 30 | 31 | cd 32 | ./setup-project.sh 33 | 34 | ***Test your installation*** 35 | 36 | In order to see if everything works well you can compile and run unit tests provided with nzmqt. Just compile ``nzmqt_test.pro`` project located under ``/src`` directory. If the build is successful you will find an ``nzmqt_test`` executable under ``/bin`` directory. Run this binary in a console and look if all tests passed. If some tests did not pass please [file a bug][nzmqt issue tracker] using the nzmqt issue tracker on GitHub. 37 | 38 | ### Setup your own project to use nzmqt 39 | 40 | There are different options for integrating nzmqt in your project. The following descriptions assume you use Qt's [QMake][] tool. 41 | 42 | ***Include File Only*** 43 | 44 | Like 0MQ's C++ binding nzmqt can be added to your project by including a single C++ header file which you need to include in your project. Consequently, using nzmqt in a Qt project is as simple as adding that single header file to your project's ``.pro`` file as follows. 45 | 46 | HEADERS += nzmqt/nzmqt.hpp 47 | 48 | Adjust your include path: 49 | 50 | INCLUDEPATH += \ 51 | /include \ 52 | /externals/include 53 | 54 | And if not already done, you also need to link against 0MQ library: 55 | 56 | LIBS += -lzmq 57 | 58 | ***Include and Source File*** 59 | 60 | If you don't like the everything is inlined as it is the case with *Include File Only* option you can use this approach. It will "move" the class method implementations to a separate ``nzmqt.cpp`` file. For this to work modify your ``.pro`` file as follows. 61 | 62 | # This define will "move" nzmqt class method 63 | # implementations to nzmqt.cpp file. 64 | DEFINES += NZMQT_LIB 65 | 66 | HEADERS += nzmqt/nzmqt.hpp 67 | SOURCES += nzmqt/nzmqt.cpp 68 | 69 | INCLUDEPATH += \ 70 | /include \ 71 | /externals/include 72 | 73 | LIBS += -lzmq 74 | 75 | ***Static Library*** 76 | 77 | You can also create an nzmqt static library. There is a project file ``nzmqt_staticlib.pro`` included in ``/src`` directory which is preconfigured to do this. 78 | 79 | ***Shared Library*** 80 | 81 | Finally, there is the option to create an nzmqt shared library. A corresponding preconfigured project file ``nzmqt_sharedlib.pro`` can also be found in ``/src``directory. 82 | 83 | Documentation 84 | ------------- 85 | 86 | * [API reference][] 87 | * [changelog][] 88 | * [software license][] 89 | * [samples][] 90 | 91 | 92 | [cppzmq]: https://github.com/zeromq/cppzmq "C++ binding for 0MQ on GitHub" 93 | [Qt 4.8.x]: http://download.qt-project.org/official_releases/qt/4.8/ "Qt 4.8.x download page" 94 | [zeromq 3.2.x]: http://www.zeromq.org/intro:get-the-software "0MQ download page" 95 | [QMake]: http://doc-snapshot.qt-project.org/qt5-stable/qmake/qmake-manual.html "Latest QMake manual" 96 | [nzmqt issue tracker]: https://github.com/jonnydee/nzmqt/issues "nzmqt issue tracker on GitHub" 97 | 98 | [API reference]: Software-API-Reference.md "nzmqt API reference" 99 | [changelog]: ../CHANGELOG.md "nzmqt software changelog" 100 | [software license]: ../LICENSE.md "nzmqt software license" 101 | [samples]: Samples.md "nzmqt samples overview" 102 | -------------------------------------------------------------------------------- /src/app/NzmqtApp.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_NZMQTAPP_H 28 | #define NZMQT_NZMQTAPP_H 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "nzmqt/nzmqt.hpp" 38 | 39 | #include "pubsub/Publisher.hpp" 40 | #include "pubsub/Subscriber.hpp" 41 | #include "reqrep/Requester.hpp" 42 | #include "reqrep/Replier.hpp" 43 | #include "pushpull/Ventilator.hpp" 44 | #include "pushpull/Worker.hpp" 45 | #include "pushpull/Sink.hpp" 46 | 47 | 48 | namespace nzmqt 49 | { 50 | 51 | namespace samples 52 | { 53 | 54 | class NzmqtApp : public QCoreApplication 55 | { 56 | Q_OBJECT 57 | 58 | typedef QCoreApplication super; 59 | 60 | public: 61 | explicit NzmqtApp(int& argc, char** argv) 62 | : super(argc, argv) 63 | { 64 | QTimer::singleShot(0, this, SLOT(run())); 65 | } 66 | 67 | bool notify(QObject *obj, QEvent *event) 68 | { 69 | try 70 | { 71 | return super::notify(obj, event); 72 | } 73 | catch (std::exception& ex) 74 | { 75 | qWarning() << ex.what(); 76 | return false; 77 | } 78 | } 79 | 80 | protected slots: 81 | void run() 82 | { 83 | QTextStream cout(stdout); 84 | try 85 | { 86 | QStringList args = arguments(); 87 | 88 | if (args.size() == 1 || "-h" == args[1] || "--help" == args[1]) 89 | { 90 | printUsage(cout); 91 | quit(); 92 | return; 93 | } 94 | 95 | QString command = args[1]; 96 | SampleBase* commandImpl = 0; 97 | 98 | ZMQContext* context = createDefaultContext(this); 99 | context->start(); 100 | 101 | if ("pubsub-publisher" == command) 102 | { 103 | if (args.size() < 4) 104 | throw std::runtime_error("Mandatory argument(s) missing!"); 105 | 106 | QString address = args[2]; 107 | QString topic = args[3]; 108 | commandImpl = new pubsub::Publisher(*context, address, topic, this); 109 | } 110 | else if ("pubsub-subscriber" == command) 111 | { 112 | if (args.size() < 4) 113 | throw std::runtime_error("Mandatory argument(s) missing!"); 114 | 115 | QString address = args[2]; 116 | QString topic = args[3]; 117 | commandImpl = new pubsub::Subscriber(*context, address, topic, this); 118 | } 119 | else if ("reqrep-replier" == command) 120 | { 121 | if (args.size() < 4) 122 | throw std::runtime_error("Mandatory argument(s) missing!"); 123 | 124 | QString address = args[2]; 125 | QString responseMsg = args[3]; 126 | commandImpl = new reqrep::Replier(*context, address, responseMsg, this); 127 | } 128 | else if ("reqrep-requester" == command) 129 | { 130 | if (args.size() < 4) 131 | throw std::runtime_error("Mandatory argument(s) missing!"); 132 | 133 | QString address = args[2]; 134 | QString requestMsg = args[3]; 135 | commandImpl = new reqrep::Requester(*context, address, requestMsg, this); 136 | } 137 | else if ("pushpull-ventilator" == command) 138 | { 139 | if (args.size() < 5) 140 | throw std::runtime_error("Mandatory argument(s) missing!"); 141 | 142 | QString ventilatorAddress = args[2]; 143 | QString sinkAddress = args[3]; 144 | quint32 numberOfWorkItems = args[4].toUInt(); 145 | commandImpl = new pushpull::Ventilator(*context, ventilatorAddress, sinkAddress, numberOfWorkItems, this); 146 | 147 | // Wait for user start. 148 | QTextStream outStream(stdout); 149 | outStream << "Press ENTER if workers and sink are ready!" << ::flush; 150 | QTextStream inStream(stdin); 151 | inStream.readLine(); 152 | } 153 | else if ("pushpull-worker" == command) 154 | { 155 | if (args.size() < 4) 156 | throw std::runtime_error("Mandatory argument(s) missing!"); 157 | 158 | QString ventilatorAddress = args[2]; 159 | QString sinkAddress = args[3]; 160 | commandImpl = new pushpull::Worker(*context, ventilatorAddress, sinkAddress, this); 161 | } 162 | else if ("pushpull-sink" == command) 163 | { 164 | if (args.size() < 3) 165 | throw std::runtime_error("Mandatory argument(s) missing!"); 166 | 167 | QString sinkAddress = args[2]; 168 | commandImpl = new pushpull::Sink(*context, sinkAddress, this); 169 | } 170 | else 171 | { 172 | throw std::runtime_error(QString("Unknown command: '%1'").arg(command).toStdString()); 173 | } 174 | 175 | // If command is finished we quit application. 176 | connect(commandImpl, SIGNAL(finished()), SLOT(quit())); 177 | // Start command. 178 | commandImpl->start(); 179 | } 180 | catch (std::exception& ex) 181 | { 182 | qWarning() << ex.what(); 183 | exit(-1); 184 | } 185 | } 186 | 187 | protected: 188 | void printUsage(QTextStream& out) 189 | { 190 | QString executable = arguments().at(0); 191 | out << QString( 192 | "\n\ 193 | USAGE: %1 [-h|--help] -- Show this help message.\n\ 194 | \n\ 195 | USAGE: %1 pubsub-publisher
-- Start PUB server.\n\ 196 | %1 pubsub-subscriber
-- Start SUB client.\n\ 197 | \n\ 198 | USAGE: %1 reqrep-replier
-- Start REQ server.\n\ 199 | %1 reqrep-requester
-- Start REP client.\n\ 200 | \n\ 201 | USAGE: %1 pushpull-ventilator -- Start ventilator.\n\ 202 | %1 pushpull-worker -- Start a worker.\n\ 203 | %1 pushpull-sink -- Start sink.\n\ 204 | \n\ 205 | Publish-Subscribe Sample:\n\ 206 | * Publisher: %1 pubsub-publisher tcp://127.0.0.1:1234 ping\n\ 207 | * Subscriber: %1 pubsub-subscriber tcp://127.0.0.1:1234 ping\n\ 208 | \n\ 209 | Request-Reply Sample:\n\ 210 | * Replier: %1 reqrep-replier tcp://127.0.0.1:1234 World\n\ 211 | * Requester: %1 reqrep-requester tcp://127.0.0.1:1234 Hello\n\ 212 | \n\ 213 | Push-Pull Sample:\n\ 214 | * Ventilator: %1 pushpull-ventilator tcp://127.0.0.1:5557 tcp://127.0.0.1:5558 100\n\ 215 | * Worker 1..n: %1 pushpull-worker tcp://127.0.0.1:5557 tcp://127.0.0.1:5558\n\ 216 | * Sink: %1 pushpull-sink tcp://127.0.0.1:5558\n\ 217 | \n").arg(executable); 218 | } 219 | }; 220 | 221 | } 222 | 223 | } 224 | 225 | #endif // NZMQT_NZMQTAPP_H 226 | -------------------------------------------------------------------------------- /src/test/nzmqt_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #include "pubsub/Publisher.hpp" 28 | #include "pubsub/Subscriber.hpp" 29 | #include "reqrep/Requester.hpp" 30 | #include "reqrep/Replier.hpp" 31 | #include "pushpull/Ventilator.hpp" 32 | #include "pushpull/Worker.hpp" 33 | #include "pushpull/Sink.hpp" 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | namespace test 40 | { 41 | 42 | class NzmqtTest : public QObject 43 | { 44 | Q_OBJECT 45 | 46 | public: 47 | NzmqtTest(); 48 | 49 | protected: 50 | QThread* makeExecutionThread(nzmqt::samples::SampleBase& sample) const; 51 | 52 | private slots: 53 | void testSignalSlotConnections(); 54 | void testPubSub(); 55 | void testReqRep(); 56 | void testPushPull(); 57 | 58 | protected slots: 59 | // Dummy slot used in test case 'testSignalSlotConnections'. 60 | void receiveMessage(const QList& msg) { Q_UNUSED(msg); } 61 | 62 | signals: 63 | // Dummy signal used in test case 'testSignalSlotConnections'. 64 | void messageSent(const QList& msg); 65 | // Dummy signal used in test case 'testSignalSlotConnections'. 66 | void messageSent(const QList& msg, nzmqt::ZMQSocket::SendFlags flags); 67 | }; 68 | 69 | NzmqtTest::NzmqtTest() 70 | { 71 | qRegisterMetaType< QList >(); 72 | } 73 | 74 | QThread* NzmqtTest::makeExecutionThread(nzmqt::samples::SampleBase& sample) const 75 | { 76 | QThread* thread = new QThread; 77 | sample.moveToThread(thread); 78 | 79 | bool connected = false; 80 | connected = connect(thread, SIGNAL(started()), &sample, SLOT(start())); Q_ASSERT(connected); 81 | connected = connect(&sample, SIGNAL(finished()), thread, SLOT(quit())); Q_ASSERT(connected); 82 | connected = connect(&sample, SIGNAL(finished()), &sample, SLOT(deleteLater())); Q_ASSERT(connected); 83 | connected = connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); Q_ASSERT(connected); 84 | 85 | return thread; 86 | } 87 | 88 | void NzmqtTest::testSignalSlotConnections() 89 | { 90 | try 91 | { 92 | QScopedPointer context(nzmqt::createDefaultContext()); 93 | nzmqt::ZMQSocket* socket = context->createSocket(nzmqt::ZMQSocket::TYP_PUB, context.data()); 94 | 95 | QVERIFY2(QObject::connect(this, SIGNAL(messageSent(QList)), 96 | socket, SLOT(sendMessage(QList))), 97 | "signal/slot connection QObject->ZMQSocket"); 98 | 99 | QVERIFY2(QObject::connect(this, SIGNAL(messageSent(QList, nzmqt::ZMQSocket::SendFlags)), 100 | socket, SLOT(sendMessage(QList, nzmqt::ZMQSocket::SendFlags))), 101 | "signal/slot connection QObject->ZMQSocket WITH SendFlags"); 102 | 103 | QVERIFY2(QObject::connect(socket, SIGNAL(messageReceived(QList)), 104 | this, SLOT(receiveMessage(QList))), 105 | "signal/slot connection ZMQSocket->QObject"); 106 | } 107 | catch (std::exception& ex) 108 | { 109 | QFAIL(ex.what()); 110 | } 111 | } 112 | 113 | void NzmqtTest::testPubSub() 114 | { 115 | using namespace nzmqt; 116 | try { 117 | QScopedPointer context(nzmqt::createDefaultContext()); 118 | 119 | // Create publisher. 120 | samples::pubsub::Publisher* publisher = new samples::pubsub::Publisher(*context, "inproc://pubsub", "ping"); 121 | QSignalSpy spyPublisherPingSent(publisher, SIGNAL(pingSent(const QList&))); 122 | QSignalSpy spyPublisherFailure(publisher, SIGNAL(failure(const QString&))); 123 | QSignalSpy spyPublisherFinished(publisher, SIGNAL(finished())); 124 | // Create publisher execution thread. 125 | QThread* publisherThread = makeExecutionThread(*publisher); 126 | QSignalSpy spyPublisherThreadFinished(publisherThread, SIGNAL(finished())); 127 | 128 | // Create subscriber. 129 | samples::pubsub::Subscriber* subscriber = new samples::pubsub::Subscriber(*context, "inproc://pubsub", "ping"); 130 | QSignalSpy spySubscriberPingReceived(subscriber, SIGNAL(pingReceived(const QList&))); 131 | QSignalSpy spySubscriberFailure(subscriber, SIGNAL(failure(const QString&))); 132 | QSignalSpy spySubscriberFinished(subscriber, SIGNAL(finished())); 133 | // Create subscriber execution thread. 134 | QThread* subscriberThread = makeExecutionThread(*subscriber); 135 | QSignalSpy spySubscriberThreadFinished(subscriberThread, SIGNAL(finished())); 136 | 137 | // 138 | // START TEST 139 | // 140 | 141 | context->start(); 142 | 143 | publisherThread->start(); 144 | QTest::qWait(500); 145 | subscriberThread->start(); 146 | 147 | QTimer::singleShot(6000, publisher, SLOT(stop())); 148 | QTimer::singleShot(6000, subscriber, SLOT(stop())); 149 | 150 | QTest::qWait(8000); 151 | 152 | // 153 | // CHECK POSTCONDITIONS 154 | // 155 | 156 | qDebug() << "Publisher pings sent:" << spyPublisherPingSent.size(); 157 | qDebug() << "Subscriber pings received:" << spySubscriberPingReceived.size(); 158 | 159 | QCOMPARE(spyPublisherFailure.size(), 0); 160 | QCOMPARE(spySubscriberFailure.size(), 0); 161 | 162 | QVERIFY2(spyPublisherPingSent.size() > 3, "Server didn't send any/enough pings."); 163 | QVERIFY2(spySubscriberPingReceived.size() > 3, "Client didn't receive any/enough pings."); 164 | 165 | QVERIFY2(qAbs(spyPublisherPingSent.size() - spySubscriberPingReceived.size()) < 3, "Publisher and subscriber communication flawed."); 166 | 167 | QCOMPARE(spyPublisherFinished.size(), 1); 168 | QCOMPARE(spySubscriberFinished.size(), 1); 169 | 170 | QCOMPARE(spyPublisherThreadFinished.size(), 1); 171 | QCOMPARE(spySubscriberThreadFinished.size(), 1); 172 | } 173 | catch (std::exception& ex) 174 | { 175 | QFAIL(ex.what()); 176 | } 177 | } 178 | 179 | void NzmqtTest::testReqRep() 180 | { 181 | using namespace nzmqt; 182 | try { 183 | QScopedPointer context(nzmqt::createDefaultContext()); 184 | 185 | // Create replier. 186 | samples::reqrep::Replier* replier = new samples::reqrep::Replier(*context, "inproc://reqrep", "world"); 187 | QSignalSpy spyReplierRequestReceived(replier, SIGNAL(requestReceived(const QList&))); 188 | QSignalSpy spyReplierReplySent(replier, SIGNAL(replySent(const QList&))); 189 | QSignalSpy spyReplierFailure(replier, SIGNAL(failure(const QString&))); 190 | QSignalSpy spyReplierFinished(replier, SIGNAL(finished())); 191 | // Create replier execution thread. 192 | QThread* replierThread = makeExecutionThread(*replier); 193 | QSignalSpy spyReplierThreadFinished(replierThread, SIGNAL(finished())); 194 | 195 | // Create requester. 196 | samples::reqrep::Requester* requester = new samples::reqrep::Requester(*context, "inproc://reqrep", "hello"); 197 | QSignalSpy spyRequesterRequestSent(requester, SIGNAL(requestSent(const QList&))); 198 | QSignalSpy spyRequesterReplyReceived(requester, SIGNAL(replyReceived(const QList&))); 199 | QSignalSpy spyRequesterFailure(requester, SIGNAL(failure(const QString&))); 200 | QSignalSpy spyRequesterFinished(requester, SIGNAL(finished())); 201 | // Create requester execution thread. 202 | QThread* requesterThread = makeExecutionThread(*requester); 203 | QSignalSpy spyRequesterThreadFinished(requesterThread, SIGNAL(finished())); 204 | 205 | // 206 | // START TEST 207 | // 208 | 209 | context->start(); 210 | 211 | replierThread->start(); 212 | QTest::qWait(500); 213 | requesterThread->start(); 214 | 215 | QTimer::singleShot(6000, replier, SLOT(stop())); 216 | QTimer::singleShot(6000, requester, SLOT(stop())); 217 | 218 | QTest::qWait(8000); 219 | 220 | // 221 | // CHECK POSTCONDITIONS 222 | // 223 | 224 | qDebug() << "Requester requests sent:" << spyRequesterRequestSent.size(); 225 | qDebug() << "Replier requests received:" << spyReplierRequestReceived.size(); 226 | 227 | QCOMPARE(spyReplierFailure.size(), 0); 228 | QCOMPARE(spyRequesterFailure.size(), 0); 229 | 230 | QVERIFY2(spyRequesterRequestSent.size() > 3, "Requester didn't send any/enough requests."); 231 | QCOMPARE(spyRequesterReplyReceived.size(), spyRequesterRequestSent.size()); 232 | 233 | QCOMPARE(spyReplierRequestReceived.size(), spyRequesterRequestSent.size()); 234 | QCOMPARE(spyReplierReplySent.size(), spyReplierRequestReceived.size()); 235 | 236 | QCOMPARE(spyReplierFinished.size(), 1); 237 | QCOMPARE(spyRequesterFinished.size(), 1); 238 | 239 | QCOMPARE(spyReplierThreadFinished.size(), 1); 240 | QCOMPARE(spyRequesterThreadFinished.size(), 1); 241 | } 242 | catch (std::exception& ex) 243 | { 244 | QFAIL(ex.what()); 245 | } 246 | } 247 | 248 | void NzmqtTest::testPushPull() 249 | { 250 | using namespace nzmqt; 251 | try { 252 | QScopedPointer context(nzmqt::createDefaultContext()); 253 | 254 | // Create ventilator. 255 | samples::pushpull::Ventilator* ventilator = new samples::pushpull::Ventilator(*context, "tcp://127.0.0.1:5557", "tcp://127.0.0.1:5558", 200); 256 | QSignalSpy spyVentilatorBatchStarted(ventilator, SIGNAL(batchStarted(int))); 257 | QSignalSpy spyVentilatorWorkItemSent(ventilator, SIGNAL(workItemSent(quint32))); 258 | QSignalSpy spyVentilatorFailure(ventilator, SIGNAL(failure(const QString&))); 259 | QSignalSpy spyVentilatorFinished(ventilator, SIGNAL(finished())); 260 | // Create ventilator execution thread. 261 | QThread* ventilatorThread = makeExecutionThread(*ventilator); 262 | QSignalSpy spyVentilatorThreadFinished(ventilatorThread, SIGNAL(finished())); 263 | 264 | // Create worker. 265 | samples::pushpull::Worker* worker = new samples::pushpull::Worker(*context, "tcp://127.0.0.1:5557", "tcp://127.0.0.1:5558"); 266 | QSignalSpy spyWorkerWorkItemReceived(worker, SIGNAL(workItemReceived(quint32))); 267 | QSignalSpy spyWorkerWorkItemResultSent(worker, SIGNAL(workItemResultSent())); 268 | QSignalSpy spyWorkerFailure(worker, SIGNAL(failure(const QString&))); 269 | QSignalSpy spyWorkerFinished(worker, SIGNAL(finished())); 270 | // Create worker execution thread. 271 | QThread* workerThread = makeExecutionThread(*worker); 272 | QSignalSpy spyWorkerThreadFinished(workerThread, SIGNAL(finished())); 273 | 274 | // Create sink. 275 | samples::pushpull::Sink* sink = new samples::pushpull::Sink(*context, "tcp://127.0.0.1:5558"); 276 | QSignalSpy spySinkBatchStarted(sink, SIGNAL(batchStarted(int))); 277 | QSignalSpy spySinkWorkItemResultReceived(sink, SIGNAL(workItemResultReceived())); 278 | QSignalSpy spySinkBatchCompleted(sink, SIGNAL(batchCompleted())); 279 | QSignalSpy spySinkFailure(sink, SIGNAL(failure(const QString&))); 280 | QSignalSpy spySinkFinished(sink, SIGNAL(finished())); 281 | // Create sink execution thread. 282 | QThread* sinkThread = makeExecutionThread(*sink); 283 | QSignalSpy spySinkThreadFinished(sinkThread, SIGNAL(finished())); 284 | 285 | // 286 | // START TEST 287 | // 288 | 289 | const int numberOfWorkItems = ventilator->numberOfWorkItems(); 290 | const int maxTotalExpectedCost = numberOfWorkItems*ventilator->maxWorkLoad(); 291 | QTimer::singleShot(maxTotalExpectedCost + 2000, ventilator, SLOT(stop())); 292 | QTimer::singleShot(maxTotalExpectedCost + 2000, worker, SLOT(stop())); 293 | QTimer::singleShot(maxTotalExpectedCost + 2000, sink, SLOT(stop())); 294 | 295 | context->start(); 296 | 297 | sinkThread->start(); 298 | QTest::qWait(500); 299 | ventilatorThread->start(); 300 | QTest::qWait(500); 301 | workerThread->start(); 302 | 303 | QTest::qWait(maxTotalExpectedCost + 2500); 304 | 305 | // 306 | // CHECK POSTCONDITIONS 307 | // 308 | 309 | QCOMPARE(spyVentilatorFailure.size(), 0); 310 | QCOMPARE(spyWorkerFailure.size(), 0); 311 | QCOMPARE(spySinkFailure.size(), 0); 312 | 313 | QCOMPARE(spyVentilatorBatchStarted.size(), 1); 314 | QCOMPARE(spyVentilatorWorkItemSent.size(), numberOfWorkItems); 315 | QCOMPARE(spyVentilatorFinished.size(), 1); 316 | QCOMPARE(spyVentilatorThreadFinished.size(), 1); 317 | 318 | QCOMPARE(spyWorkerWorkItemReceived.size(), numberOfWorkItems); 319 | QCOMPARE(spyWorkerWorkItemResultSent.size(), numberOfWorkItems); 320 | QCOMPARE(spyWorkerFinished.size(), 1); 321 | QCOMPARE(spyWorkerThreadFinished.size(), 1); 322 | 323 | QCOMPARE(spySinkBatchStarted.size(), 1); 324 | QCOMPARE(spySinkWorkItemResultReceived.size(), numberOfWorkItems); 325 | QCOMPARE(spySinkBatchCompleted.size(), 1); 326 | QCOMPARE(spySinkFinished.size(), 1); 327 | QCOMPARE(spySinkThreadFinished.size(), 1); 328 | } 329 | catch (std::exception& ex) 330 | { 331 | QFAIL(ex.what()); 332 | } 333 | } 334 | 335 | } 336 | 337 | QTEST_MAIN(test::NzmqtTest) 338 | 339 | #include "nzmqt_test.moc" 340 | -------------------------------------------------------------------------------- /include/nzmqt/impl.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_IMPL_HPP 28 | #define NZMQT_IMPL_HPP 29 | 30 | #include "nzmqt/nzmqt.hpp" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #if defined(NZMQT_LIB) 39 | // #pragma message("nzmqt is built as library") 40 | #define NZMQT_INLINE 41 | #else 42 | // #pragma message("nzmqt is built inline") 43 | #define NZMQT_INLINE inline 44 | #endif 45 | 46 | namespace nzmqt 47 | { 48 | 49 | /* 50 | * ZMQMessage 51 | */ 52 | 53 | NZMQT_INLINE ZMQMessage::ZMQMessage() 54 | : super() 55 | { 56 | } 57 | 58 | NZMQT_INLINE ZMQMessage::ZMQMessage(size_t size_) 59 | : super(size_) 60 | { 61 | } 62 | 63 | NZMQT_INLINE ZMQMessage::ZMQMessage(void* data_, size_t size_, free_fn *ffn_, void* hint_) 64 | : super(data_, size_, ffn_, hint_) 65 | { 66 | } 67 | 68 | NZMQT_INLINE ZMQMessage::ZMQMessage(const QByteArray& b) 69 | : super(b.size()) 70 | { 71 | memcpy(data(), b.constData(), b.size()); 72 | } 73 | 74 | NZMQT_INLINE void ZMQMessage::move(ZMQMessage* msg_) 75 | { 76 | super::move(static_cast(msg_)); 77 | } 78 | 79 | NZMQT_INLINE void ZMQMessage::clone(ZMQMessage* msg_) 80 | { 81 | rebuild(msg_->size()); 82 | memcpy(data(), msg_->data(), size()); 83 | } 84 | 85 | NZMQT_INLINE QByteArray ZMQMessage::toByteArray() 86 | { 87 | return size() <= INT_MAX ? QByteArray(data(), int(size())) : QByteArray(); 88 | } 89 | 90 | 91 | 92 | /* 93 | * ZMQSocket 94 | */ 95 | 96 | NZMQT_INLINE ZMQSocket::ZMQSocket(ZMQContext* context_, Type type_) 97 | : qsuper(nullptr) 98 | , zmqsuper(*context_, type_) 99 | , m_context(context_) 100 | { 101 | } 102 | 103 | NZMQT_INLINE ZMQSocket::~ZMQSocket() 104 | { 105 | // qDebug() << Q_FUNC_INFO << "Context:" << m_context; 106 | close(); 107 | } 108 | 109 | NZMQT_INLINE void ZMQSocket::close() 110 | { 111 | // qDebug() << Q_FUNC_INFO << "Context:" << m_context; 112 | if (m_context) 113 | { 114 | m_context->unregisterSocket(this); 115 | m_context = nullptr; 116 | } 117 | zmqsuper::close(); 118 | } 119 | 120 | NZMQT_INLINE void ZMQSocket::setOption(Option optName_, const void *optionVal_, size_t optionValLen_) 121 | { 122 | setsockopt(optName_, optionVal_, optionValLen_); 123 | } 124 | 125 | NZMQT_INLINE void ZMQSocket::setOption(Option optName_, const char* str_) 126 | { 127 | setOption(optName_, str_, strlen(str_)); 128 | } 129 | 130 | NZMQT_INLINE void ZMQSocket::setOption(Option optName_, const QByteArray& bytes_) 131 | { 132 | setOption(optName_, bytes_.constData(), bytes_.size()); 133 | } 134 | 135 | NZMQT_INLINE void ZMQSocket::getOption(Option option_, void *optval_, size_t *optvallen_) const 136 | { 137 | const_cast(this)->getsockopt(option_, optval_, optvallen_); 138 | } 139 | 140 | NZMQT_INLINE void ZMQSocket::bindTo(const QString& addr_) 141 | { 142 | bind(addr_.toLocal8Bit()); 143 | } 144 | 145 | NZMQT_INLINE void ZMQSocket::bindTo(const char *addr_) 146 | { 147 | bind(addr_); 148 | } 149 | 150 | NZMQT_INLINE void ZMQSocket::unbindFrom(const QString& addr_) 151 | { 152 | unbind(addr_.toLocal8Bit()); 153 | } 154 | 155 | NZMQT_INLINE void ZMQSocket::unbindFrom(const char *addr_) 156 | { 157 | unbind(addr_); 158 | } 159 | 160 | NZMQT_INLINE void ZMQSocket::connectTo(const QString& addr_) 161 | { 162 | zmqsuper::connect(addr_.toLocal8Bit()); 163 | } 164 | 165 | NZMQT_INLINE void ZMQSocket::connectTo(const char* addr_) 166 | { 167 | zmqsuper::connect(addr_); 168 | } 169 | 170 | NZMQT_INLINE void ZMQSocket::disconnectFrom(const QString& addr_) 171 | { 172 | zmqsuper::disconnect(addr_.toLocal8Bit()); 173 | } 174 | 175 | NZMQT_INLINE void ZMQSocket::disconnectFrom(const char* addr_) 176 | { 177 | zmqsuper::disconnect(addr_); 178 | } 179 | 180 | NZMQT_INLINE bool ZMQSocket::sendMessage(ZMQMessage& msg_, SendFlags flags_) 181 | { 182 | return send(msg_, flags_); 183 | } 184 | 185 | NZMQT_INLINE bool ZMQSocket::sendMessage(const QByteArray& bytes_, SendFlags flags_) 186 | { 187 | ZMQMessage msg(bytes_); 188 | return send(msg, flags_); 189 | } 190 | 191 | NZMQT_INLINE bool ZMQSocket::sendMessage(const QList& msg_, SendFlags flags_) 192 | { 193 | int i; 194 | for (i = 0; i < msg_.size() - 1; i++) 195 | { 196 | if (!sendMessage(msg_[i], flags_ | SND_MORE)) 197 | return false; 198 | } 199 | if (i < msg_.size()) 200 | return sendMessage(msg_[i], flags_); 201 | 202 | return true; 203 | } 204 | 205 | NZMQT_INLINE bool ZMQSocket::receiveMessage(ZMQMessage* msg_, ReceiveFlags flags_) 206 | { 207 | return recv(msg_, flags_); 208 | } 209 | 210 | NZMQT_INLINE QList ZMQSocket::receiveMessage(ReceiveFlags flags_) 211 | { 212 | QList parts; 213 | 214 | ZMQMessage msg; 215 | while (receiveMessage(&msg, flags_)) 216 | { 217 | parts += msg.toByteArray(); 218 | msg.rebuild(); 219 | 220 | if (!hasMoreMessageParts()) 221 | break; 222 | } 223 | 224 | return parts; 225 | } 226 | 227 | NZMQT_INLINE QList< QList > ZMQSocket::receiveMessages(ReceiveFlags flags_) 228 | { 229 | QList< QList > ret; 230 | 231 | QList parts = receiveMessage(flags_); 232 | while (!parts.isEmpty()) 233 | { 234 | ret += std::move(parts); 235 | 236 | parts = receiveMessage(flags_); 237 | } 238 | 239 | return ret; 240 | } 241 | 242 | NZMQT_INLINE qintptr ZMQSocket::fileDescriptor() const 243 | { 244 | qintptr value; 245 | size_t size = sizeof(value); 246 | getOption(OPT_FD, &value, &size); 247 | return value; 248 | } 249 | 250 | NZMQT_INLINE ZMQSocket::Events ZMQSocket::events() const 251 | { 252 | qint32 value; 253 | size_t size = sizeof(value); 254 | getOption(OPT_EVENTS, &value, &size); 255 | return static_cast(value); 256 | } 257 | 258 | // Returns true if there are more parts of a multi-part message 259 | // to be received. 260 | NZMQT_INLINE bool ZMQSocket::hasMoreMessageParts() const 261 | { 262 | qint32 value; 263 | size_t size = sizeof(value); 264 | getOption(OPT_RCVMORE, &value, &size); 265 | return value; 266 | } 267 | 268 | NZMQT_INLINE void ZMQSocket::setIdentity(const char* nameStr_) 269 | { 270 | setOption(OPT_IDENTITY, nameStr_); 271 | } 272 | 273 | NZMQT_INLINE void ZMQSocket::setIdentity(const QString& name_) 274 | { 275 | setOption(OPT_IDENTITY, name_.toLocal8Bit()); 276 | } 277 | 278 | NZMQT_INLINE void ZMQSocket::setIdentity(const QByteArray& name_) 279 | { 280 | setOption(OPT_IDENTITY, const_cast(name_.constData()), name_.size()); 281 | } 282 | 283 | NZMQT_INLINE QByteArray ZMQSocket::identity() const 284 | { 285 | char idbuf[256]; 286 | size_t size = sizeof(idbuf); 287 | getOption(OPT_IDENTITY, idbuf, &size); 288 | return QByteArray(idbuf, int(size)); 289 | } 290 | 291 | NZMQT_INLINE void ZMQSocket::setLinger(int msec_) 292 | { 293 | setOption(OPT_LINGER, msec_); 294 | } 295 | 296 | NZMQT_INLINE qint32 ZMQSocket::linger() const 297 | { 298 | qint32 msec=-1; 299 | size_t size = sizeof(msec); 300 | getOption(OPT_LINGER, &msec, &size); 301 | return msec; 302 | } 303 | 304 | NZMQT_INLINE void ZMQSocket::subscribeTo(const char* filterStr_) 305 | { 306 | setOption(OPT_SUBSCRIBE, filterStr_); 307 | } 308 | 309 | NZMQT_INLINE void ZMQSocket::subscribeTo(const QString& filter_) 310 | { 311 | setOption(OPT_SUBSCRIBE, filter_.toLocal8Bit()); 312 | } 313 | 314 | NZMQT_INLINE void ZMQSocket::subscribeTo(const QByteArray& filter_) 315 | { 316 | setOption(OPT_SUBSCRIBE, filter_); 317 | } 318 | 319 | NZMQT_INLINE void ZMQSocket::unsubscribeFrom(const char* filterStr_) 320 | { 321 | setOption(OPT_UNSUBSCRIBE, filterStr_); 322 | } 323 | 324 | NZMQT_INLINE void ZMQSocket::unsubscribeFrom(const QString& filter_) 325 | { 326 | setOption(OPT_UNSUBSCRIBE, filter_.toLocal8Bit()); 327 | } 328 | 329 | NZMQT_INLINE void ZMQSocket::unsubscribeFrom(const QByteArray& filter_) 330 | { 331 | setOption(OPT_UNSUBSCRIBE, filter_); 332 | } 333 | 334 | NZMQT_INLINE void ZMQSocket::setSendHighWaterMark(int value_) 335 | { 336 | setOption(OPT_SNDHWM, value_); 337 | } 338 | 339 | NZMQT_INLINE void ZMQSocket::setReceiveHighWaterMark(int value_) 340 | { 341 | setOption(OPT_RCVHWM, value_); 342 | } 343 | 344 | NZMQT_INLINE bool ZMQSocket::isConnected() 345 | { 346 | return const_cast(this)->connected(); 347 | } 348 | 349 | /* 350 | * ZMQContext 351 | */ 352 | 353 | NZMQT_INLINE ZMQContext::ZMQContext(QObject* parent_, int io_threads_) 354 | : qsuper(parent_) 355 | , zmqsuper(io_threads_) 356 | { 357 | } 358 | 359 | NZMQT_INLINE ZMQContext::~ZMQContext() 360 | { 361 | // qDebug() << Q_FUNC_INFO << "Sockets:" << m_sockets; 362 | for(ZMQSocket* socket : registeredSockets()) 363 | { 364 | socket->m_context = nullptr; 365 | // As stated by 0MQ, close() must ONLY be called from the thread 366 | // owning the socket. So we use 'invokeMethod' which (hopefully) 367 | // results in a 'close' call from within the socket's thread. 368 | QMetaObject::invokeMethod(socket, "close"); 369 | } 370 | } 371 | 372 | NZMQT_INLINE ZMQSocket* ZMQContext::createSocket(ZMQSocket::Type type_, QObject* parent_) 373 | { 374 | ZMQSocket* socket = createSocketInternal(type_); 375 | registerSocket(socket); 376 | socket->setParent(parent_); 377 | return socket; 378 | } 379 | 380 | NZMQT_INLINE void ZMQContext::registerSocket(ZMQSocket* socket_) 381 | { 382 | m_sockets.push_back(socket_); 383 | } 384 | 385 | NZMQT_INLINE void ZMQContext::unregisterSocket(ZMQSocket* socket_) 386 | { 387 | for(Sockets::iterator soIt = m_sockets.begin(); soIt != m_sockets.end(); ++soIt) 388 | { 389 | if (*soIt == socket_) 390 | { 391 | m_sockets.erase(soIt); 392 | break; 393 | } 394 | } 395 | } 396 | 397 | NZMQT_INLINE const ZMQContext::Sockets& ZMQContext::registeredSockets() const 398 | { 399 | return m_sockets; 400 | } 401 | 402 | 403 | 404 | /* 405 | * ZMQDevice 406 | */ 407 | /* 408 | NZMQT_INLINE ZMQDevice::ZMQDevice(Type type, ZMQSocket* frontend, ZMQSocket* backend) 409 | : type_(type) 410 | , frontend_(frontend) 411 | , backend_(backend) 412 | { 413 | } 414 | 415 | NZMQT_INLINE void ZMQDevice::run() 416 | { 417 | zmq::device(type_, *frontend_, *backend_); 418 | } 419 | */ 420 | 421 | 422 | /* 423 | * PollingZMQSocket 424 | */ 425 | 426 | NZMQT_INLINE PollingZMQSocket::PollingZMQSocket(PollingZMQContext* context_, Type type_) 427 | : super(context_, type_) 428 | { 429 | } 430 | 431 | 432 | /* 433 | * PollingZMQContext 434 | */ 435 | 436 | NZMQT_INLINE PollingZMQContext::PollingZMQContext(QObject* parent_, int io_threads_) 437 | : super(parent_, io_threads_) 438 | , m_pollItemsMutex(QMutex::Recursive) 439 | , m_interval(NZMQT_POLLINGZMQCONTEXT_DEFAULT_POLLINTERVAL) 440 | , m_stopped(false) 441 | { 442 | setAutoDelete(false); 443 | } 444 | 445 | NZMQT_INLINE void PollingZMQContext::setInterval(int interval_) 446 | { 447 | m_interval = interval_; 448 | } 449 | 450 | NZMQT_INLINE int PollingZMQContext::getInterval() const 451 | { 452 | return m_interval; 453 | } 454 | 455 | NZMQT_INLINE void PollingZMQContext::start() 456 | { 457 | m_stopped = false; 458 | QTimer::singleShot(0, this, &PollingZMQContext::run); 459 | } 460 | 461 | NZMQT_INLINE void PollingZMQContext::stop() 462 | { 463 | m_stopped = true; 464 | } 465 | 466 | NZMQT_INLINE bool PollingZMQContext::isStopped() const 467 | { 468 | return m_stopped; 469 | } 470 | 471 | NZMQT_INLINE void PollingZMQContext::run() 472 | { 473 | if (m_stopped) 474 | return; 475 | 476 | try 477 | { 478 | poll(); 479 | } 480 | catch (const ZMQException& ex) 481 | { 482 | qWarning("Exception during poll: %s", ex.what()); 483 | emit pollError(ex.num(), ex.what()); 484 | } 485 | 486 | if (!m_stopped) 487 | QTimer::singleShot(m_interval, this, &PollingZMQContext::run); 488 | } 489 | 490 | NZMQT_INLINE void PollingZMQContext::poll(long timeout_) 491 | { 492 | int cnt; 493 | do { 494 | QMutexLocker lock(&m_pollItemsMutex); 495 | 496 | if (m_pollItems.empty()) 497 | return; 498 | 499 | cnt = zmq::poll(&m_pollItems[0], m_pollItems.size(), timeout_); 500 | Q_ASSERT_X(cnt >= 0, Q_FUNC_INFO, "A value < 0 should be reflected by an exception."); 501 | if (0 == cnt) 502 | return; 503 | 504 | PollItems::iterator poIt = m_pollItems.begin(); 505 | ZMQContext::Sockets::const_iterator soIt = registeredSockets().begin(); 506 | int i = 0; 507 | while (i < cnt && poIt != m_pollItems.end()) 508 | { 509 | if (poIt->revents & ZMQSocket::EVT_POLLIN) 510 | { 511 | PollingZMQSocket* socket = static_cast(*soIt); 512 | QList && message = socket->receiveMessage(); 513 | socket->messageReceived(std::move(message)); 514 | i++; 515 | } 516 | ++soIt; 517 | ++poIt; 518 | } 519 | } while (cnt > 0); 520 | } 521 | 522 | NZMQT_INLINE PollingZMQSocket* PollingZMQContext::createSocketInternal(ZMQSocket::Type type_) 523 | { 524 | return new PollingZMQSocket(this, type_); 525 | } 526 | 527 | NZMQT_INLINE void PollingZMQContext::registerSocket(ZMQSocket* socket_) 528 | { 529 | pollitem_t pollItem = { *socket_, 0, ZMQSocket::EVT_POLLIN, 0 }; 530 | 531 | QMutexLocker lock(&m_pollItemsMutex); 532 | 533 | m_pollItems.push_back(pollItem); 534 | 535 | super::registerSocket(socket_); 536 | } 537 | 538 | NZMQT_INLINE void PollingZMQContext::unregisterSocket(ZMQSocket* socket_) 539 | { 540 | QMutexLocker lock(&m_pollItemsMutex); 541 | 542 | PollItems::iterator poIt = m_pollItems.begin(); 543 | ZMQContext::Sockets::const_iterator soIt = registeredSockets().begin(); 544 | while (soIt != registeredSockets().end()) 545 | { 546 | if (*soIt == socket_) 547 | { 548 | m_pollItems.erase(poIt); 549 | break; 550 | } 551 | ++soIt; 552 | ++poIt; 553 | } 554 | 555 | super::unregisterSocket(socket_); 556 | } 557 | 558 | 559 | 560 | /* 561 | * SocketNotifierZMQSocket 562 | */ 563 | 564 | NZMQT_INLINE SocketNotifierZMQSocket::SocketNotifierZMQSocket(ZMQContext* context_, Type type_) 565 | : super(context_, type_) 566 | , socketNotifyRead_(0) 567 | , socketNotifyWrite_(0) 568 | { 569 | qintptr fd = fileDescriptor(); 570 | 571 | socketNotifyRead_ = new QSocketNotifier(fd, QSocketNotifier::Read, this); 572 | QObject::connect(socketNotifyRead_, &QSocketNotifier::activated, this, &SocketNotifierZMQSocket::socketReadActivity); 573 | 574 | socketNotifyWrite_ = new QSocketNotifier(fd, QSocketNotifier::Write, this); 575 | QObject::connect(socketNotifyWrite_, &QSocketNotifier::activated, this, &SocketNotifierZMQSocket::socketWriteActivity); 576 | } 577 | 578 | NZMQT_INLINE SocketNotifierZMQSocket::~SocketNotifierZMQSocket() 579 | { 580 | close(); 581 | } 582 | 583 | NZMQT_INLINE void SocketNotifierZMQSocket::close() 584 | { 585 | socketNotifyRead_->deleteLater(); 586 | socketNotifyWrite_->deleteLater(); 587 | super::close(); 588 | } 589 | 590 | NZMQT_INLINE void SocketNotifierZMQSocket::socketReadActivity() 591 | { 592 | socketNotifyRead_->setEnabled(false); 593 | 594 | try 595 | { 596 | while(isConnected() && (events() & EVT_POLLIN)) 597 | { 598 | const QList & message = receiveMessage(); 599 | emit messageReceived(message); 600 | } 601 | } 602 | catch (const ZMQException& ex) 603 | { 604 | qWarning("Exception during read: %s", ex.what()); 605 | emit notifierError(ex.num(), ex.what()); 606 | } 607 | 608 | socketNotifyRead_->setEnabled(true); 609 | } 610 | 611 | NZMQT_INLINE void SocketNotifierZMQSocket::socketWriteActivity() 612 | { 613 | socketNotifyWrite_->setEnabled(false); 614 | 615 | try 616 | { 617 | while (isConnected() && (events() & EVT_POLLIN)) 618 | { 619 | const QList & message = receiveMessage(); 620 | emit messageReceived(message); 621 | } 622 | } 623 | catch (const ZMQException& ex) 624 | { 625 | qWarning("Exception during write: %s", ex.what()); 626 | emit notifierError(ex.num(), ex.what()); 627 | } 628 | 629 | socketNotifyWrite_->setEnabled(true); 630 | } 631 | 632 | 633 | 634 | /* 635 | * SocketNotifierZMQContext 636 | */ 637 | 638 | NZMQT_INLINE SocketNotifierZMQContext::SocketNotifierZMQContext(QObject* parent_, int io_threads_) 639 | : super(parent_, io_threads_) 640 | { 641 | } 642 | 643 | NZMQT_INLINE void SocketNotifierZMQContext::start() 644 | { 645 | } 646 | 647 | NZMQT_INLINE void SocketNotifierZMQContext::stop() 648 | { 649 | } 650 | 651 | NZMQT_INLINE bool SocketNotifierZMQContext::isStopped() const 652 | { 653 | return false; 654 | } 655 | 656 | NZMQT_INLINE SocketNotifierZMQSocket* SocketNotifierZMQContext::createSocketInternal(ZMQSocket::Type type_) 657 | { 658 | SocketNotifierZMQSocket *socket = new SocketNotifierZMQSocket(this, type_); 659 | connect(socket, &SocketNotifierZMQSocket::notifierError, 660 | this, &SocketNotifierZMQContext::notifierError); 661 | return socket; 662 | } 663 | 664 | } 665 | 666 | #endif // NZMQT_IMPL_HPP 667 | -------------------------------------------------------------------------------- /include/nzmqt/nzmqt.hpp: -------------------------------------------------------------------------------- 1 | // Copyright 2011-2014 Johann Duscher (a.k.a. Jonny Dee). All rights reserved. 2 | // 3 | // Redistribution and use in source and binary forms, with or without modification, are 4 | // permitted provided that the following conditions are met: 5 | // 6 | // 1. Redistributions of source code must retain the above copyright notice, this list of 7 | // conditions and the following disclaimer. 8 | // 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | // of conditions and the following disclaimer in the documentation and/or other materials 11 | // provided with the distribution. 12 | // 13 | // THIS SOFTWARE IS PROVIDED BY JOHANN DUSCHER ''AS IS'' AND ANY EXPRESS OR IMPLIED 14 | // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR 16 | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | // 23 | // The views and conclusions contained in the software and documentation are those of the 24 | // authors and should not be interpreted as representing official policies, either expressed 25 | // or implied, of Johann Duscher. 26 | 27 | #ifndef NZMQT_H 28 | #define NZMQT_H 29 | 30 | #include "nzmqt/global.hpp" 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | 45 | // Define default context implementation to be used. 46 | #ifndef NZMQT_DEFAULT_ZMQCONTEXT_IMPLEMENTATION 47 | #define NZMQT_DEFAULT_ZMQCONTEXT_IMPLEMENTATION PollingZMQContext 48 | //#define NZMQT_DEFAULT_ZMQCONTEXT_IMPLEMENTATION SocketNotifierZMQContext 49 | #endif 50 | 51 | // Define default number of IO threads to be used by ZMQ. 52 | #ifndef NZMQT_DEFAULT_IOTHREADS 53 | #define NZMQT_DEFAULT_IOTHREADS 4 54 | #endif 55 | 56 | // Define default poll interval for polling-based implementation. 57 | #ifndef NZMQT_POLLINGZMQCONTEXT_DEFAULT_POLLINTERVAL 58 | #define NZMQT_POLLINGZMQCONTEXT_DEFAULT_POLLINTERVAL 10 /* msec */ 59 | #endif 60 | 61 | class QSocketNotifier; 62 | 63 | namespace nzmqt 64 | { 65 | typedef zmq::free_fn free_fn; 66 | typedef zmq::pollitem_t pollitem_t; 67 | 68 | typedef zmq::error_t ZMQException; 69 | 70 | using zmq::poll; 71 | using zmq::version; 72 | 73 | // This class wraps ZMQ's message structure. 74 | class NZMQT_API ZMQMessage : private zmq::message_t 75 | { 76 | friend class ZMQSocket; 77 | 78 | typedef zmq::message_t super; 79 | 80 | public: 81 | ZMQMessage(); 82 | 83 | ZMQMessage(size_t size_); 84 | 85 | ZMQMessage(void* data_, size_t size_, free_fn *ffn_, void* hint_ = 0); 86 | 87 | ZMQMessage(const QByteArray& b); 88 | 89 | using super::rebuild; 90 | 91 | void move(ZMQMessage* msg_); 92 | 93 | using super::copy; 94 | 95 | using super::more; 96 | 97 | void clone(ZMQMessage* msg_); 98 | 99 | using super::data; 100 | 101 | using super::size; 102 | 103 | QByteArray toByteArray(); 104 | }; 105 | 106 | class ZMQContext; 107 | 108 | // This class cannot be instantiated. Its purpose is to serve as an 109 | // intermediate base class that provides Qt-based convenience methods 110 | // to subclasses. 111 | class NZMQT_API ZMQSocket : public QObject, private zmq::socket_t 112 | { 113 | Q_OBJECT 114 | Q_ENUMS(Type Event SendFlag ReceiveFlag Option) 115 | Q_FLAGS(Event Events) 116 | Q_FLAGS(SendFlag SendFlags) 117 | Q_FLAGS(ReceiveFlag ReceiveFlags) 118 | 119 | typedef QObject qsuper; 120 | typedef zmq::socket_t zmqsuper; 121 | 122 | public: 123 | enum Type 124 | { 125 | TYP_PUB = ZMQ_PUB, 126 | TYP_SUB = ZMQ_SUB, 127 | TYP_PUSH = ZMQ_PUSH, 128 | TYP_PULL = ZMQ_PULL, 129 | TYP_REQ = ZMQ_REQ, 130 | TYP_REP = ZMQ_REP, 131 | TYP_DEALER = ZMQ_DEALER, 132 | TYP_ROUTER = ZMQ_ROUTER, 133 | TYP_PAIR = ZMQ_PAIR, 134 | TYP_XPUB = ZMQ_XPUB, 135 | TYP_XSUB = ZMQ_XSUB 136 | }; 137 | 138 | enum Event : int 139 | { 140 | EVT_POLLIN = ZMQ_POLLIN, 141 | EVT_POLLOUT = ZMQ_POLLOUT, 142 | EVT_POLLERR = ZMQ_POLLERR 143 | }; 144 | Q_DECLARE_FLAGS(Events, Event) 145 | 146 | enum SendFlag : int 147 | { 148 | SND_MORE = ZMQ_SNDMORE, 149 | SND_DONTWAIT = ZMQ_DONTWAIT 150 | }; 151 | Q_DECLARE_FLAGS(SendFlags, SendFlag) 152 | 153 | enum ReceiveFlag : int 154 | { 155 | RCV_DONTWAIT = ZMQ_DONTWAIT 156 | }; 157 | Q_DECLARE_FLAGS(ReceiveFlags, ReceiveFlag) 158 | 159 | enum Option 160 | { 161 | // Get only. 162 | OPT_TYPE = ZMQ_TYPE, 163 | OPT_RCVMORE = ZMQ_RCVMORE, 164 | OPT_FD = ZMQ_FD, 165 | OPT_EVENTS = ZMQ_EVENTS, 166 | OPT_MAXMSGSIZE = ZMQ_MAXMSGSIZE, 167 | 168 | // Set only. 169 | OPT_SUBSCRIBE = ZMQ_SUBSCRIBE, 170 | OPT_UNSUBSCRIBE = ZMQ_UNSUBSCRIBE, 171 | #ifdef ZMQ_IMMEDIATE 172 | OPT_IMMEDIATE = ZMQ_IMMEDIATE, 173 | #endif 174 | #ifdef ZMQ_REQ_CORRELATE 175 | OPT_REQ_CORRELATE = ZMQ_REQ_CORRELATE, 176 | #endif 177 | #ifdef ZMQ_REQ_RELAXED 178 | OPT_REQ_RELAXED = ZMQ_REQ_RELAXED, 179 | #endif 180 | 181 | // Get and set. 182 | OPT_AFFINITY = ZMQ_AFFINITY, 183 | OPT_IDENTITY = ZMQ_IDENTITY, 184 | OPT_RATE = ZMQ_RATE, 185 | OPT_RECOVERY_IVL = ZMQ_RECOVERY_IVL, 186 | OPT_SNDBUF = ZMQ_SNDBUF, 187 | OPT_RCVBUF = ZMQ_RCVBUF, 188 | OPT_LINGER = ZMQ_LINGER, 189 | OPT_RECONNECT_IVL = ZMQ_RECONNECT_IVL, 190 | OPT_RECONNECT_IVL_MAX = ZMQ_RECONNECT_IVL_MAX, 191 | OPT_BACKLOG = ZMQ_BACKLOG, 192 | OPT_SNDHWM = ZMQ_SNDHWM, 193 | OPT_RCVHWM = ZMQ_RCVHWM, 194 | OPT_SNDTIMEO = ZMQ_SNDTIMEO, 195 | OPT_RCVTIMEO = ZMQ_RCVTIMEO, 196 | #ifdef ZMQ_IPV6 197 | OPT_IPV6 = ZMQ_IPV6, 198 | #endif 199 | #ifdef ZMQ_CONFLATE 200 | OPT_CONFLATE = ZMQ_CONFLATE, 201 | #endif 202 | #ifdef ZMQ_TOS 203 | OPT_TOS = ZMQ_TOS, 204 | #endif 205 | }; 206 | 207 | ~ZMQSocket(); 208 | 209 | using zmqsuper::operator void *; 210 | 211 | void setOption(Option optName_, const void *optionVal_, size_t optionValLen_); 212 | 213 | void setOption(Option optName_, const char* str_); 214 | 215 | void setOption(Option optName_, const QByteArray& bytes_); 216 | 217 | template::value>::type> 218 | void setOption(Option optName_, INT_T value_) 219 | { 220 | setOption(optName_, &value_, sizeof(value_)); 221 | } 222 | 223 | void getOption(Option option_, void *optval_, size_t *optvallen_) const; 224 | 225 | void bindTo(const QString& addr_); 226 | 227 | void bindTo(const char *addr_); 228 | 229 | void unbindFrom(const QString& addr_); 230 | 231 | void unbindFrom(const char *addr_); 232 | 233 | void connectTo(const QString& addr_); 234 | 235 | void connectTo(const char* addr_); 236 | 237 | void disconnectFrom(const QString& addr_); 238 | 239 | void disconnectFrom(const char* addr_); 240 | 241 | bool sendMessage(ZMQMessage& msg_, SendFlags flags_ = SND_DONTWAIT); 242 | 243 | // Receives a message or a message part. 244 | bool receiveMessage(ZMQMessage* msg_, ReceiveFlags flags_ = RCV_DONTWAIT); 245 | 246 | // Receives a message. 247 | // The message is represented as a list of byte arrays representing 248 | // a message's parts. If the message is not a multi-part message the 249 | // list will only contain one array. 250 | QList receiveMessage(ReceiveFlags flags_ = RCV_DONTWAIT); 251 | 252 | // Receives all messages currently available. 253 | // Each message is represented as a list of byte arrays representing the messages 254 | // and their parts in case of multi-part messages. If a message isn't a multi-part 255 | // message the corresponding byte array list will only contain one element. 256 | // Note that this method won't work with REQ-REP protocol. 257 | QList< QList > receiveMessages(ReceiveFlags flags_ = RCV_DONTWAIT); 258 | 259 | qintptr fileDescriptor() const; 260 | 261 | Events events() const; 262 | 263 | // Returns true if there are more parts of a multi-part message 264 | // to be received. 265 | bool hasMoreMessageParts() const; 266 | 267 | void setIdentity(const char* nameStr_); 268 | 269 | void setIdentity(const QString& name_); 270 | 271 | void setIdentity(const QByteArray& name_); 272 | 273 | QByteArray identity() const; 274 | 275 | void setLinger(int msec_); 276 | 277 | qint32 linger() const; 278 | 279 | void subscribeTo(const char* filterStr_); 280 | 281 | void subscribeTo(const QString& filter_); 282 | 283 | void subscribeTo(const QByteArray& filter_); 284 | 285 | void unsubscribeFrom(const char* filterStr_); 286 | 287 | void unsubscribeFrom(const QString& filter_); 288 | 289 | void unsubscribeFrom(const QByteArray& filter_); 290 | 291 | void setSendHighWaterMark(int value_); 292 | 293 | void setReceiveHighWaterMark(int value_); 294 | 295 | bool isConnected(); 296 | 297 | signals: 298 | void messageReceived(const QList&); 299 | 300 | public slots: 301 | void close(); 302 | 303 | // Send the given bytes as a single-part message. 304 | bool sendMessage(const QByteArray& bytes_, nzmqt::ZMQSocket::SendFlags flags_ = SND_DONTWAIT); 305 | 306 | // Interprets the provided list of byte arrays as a multi-part message 307 | // and sends them accordingly. 308 | // If an empty list is provided this method doesn't do anything and returns trua. 309 | bool sendMessage(const QList& msg_, nzmqt::ZMQSocket::SendFlags flags_ = SND_DONTWAIT); 310 | 311 | 312 | protected: 313 | ZMQSocket(ZMQContext* context_, Type type_); 314 | 315 | private: 316 | friend class ZMQContext; 317 | 318 | ZMQContext* m_context; 319 | }; 320 | Q_DECLARE_OPERATORS_FOR_FLAGS(ZMQSocket::Events) 321 | Q_DECLARE_OPERATORS_FOR_FLAGS(ZMQSocket::SendFlags) 322 | Q_DECLARE_OPERATORS_FOR_FLAGS(ZMQSocket::ReceiveFlags) 323 | 324 | 325 | // This class is an abstract base class for concrete implementations. 326 | class NZMQT_API ZMQContext : public QObject, private zmq::context_t 327 | { 328 | Q_OBJECT 329 | 330 | typedef QObject qsuper; 331 | typedef zmq::context_t zmqsuper; 332 | 333 | friend class ZMQSocket; 334 | 335 | public: 336 | ZMQContext(QObject* parent_ = nullptr, int io_threads_ = NZMQT_DEFAULT_IOTHREADS); 337 | 338 | // Deleting children is necessary, because otherwise the children are deleted after the context 339 | // which results in a blocking state. So we delete the children before the zmq::context_t 340 | // destructor implementation is called. 341 | ~ZMQContext(); 342 | 343 | using zmqsuper::operator void*; 344 | 345 | // Creates a socket instance of the specified type and parent. 346 | // The created instance will have the specified parent 347 | // (as usual you can also call 'ZMQSocket::setParent()' method to change 348 | // ownership later on). Make sure, however, that the socket's parent 349 | // belongs to the same thread as the socket instance itself (as it is required 350 | // by Qt). Otherwise, you will encounter strange errors. 351 | ZMQSocket* createSocket(ZMQSocket::Type type_, QObject* parent_ = nullptr); 352 | 353 | // Start watching for incoming messages. 354 | virtual void start() = 0; 355 | 356 | // Stop watching for incoming messages. 357 | virtual void stop() = 0; 358 | 359 | // Indicates if watching for incoming messages is enabled. 360 | virtual bool isStopped() const = 0; 361 | 362 | protected: 363 | typedef QVector Sockets; 364 | 365 | // Creates a socket instance of the specified type. 366 | virtual ZMQSocket* createSocketInternal(ZMQSocket::Type type_) = 0; 367 | 368 | virtual void registerSocket(ZMQSocket* socket_); 369 | 370 | // Remove the given socket object from the list of registered sockets. 371 | virtual void unregisterSocket(ZMQSocket* socket_); 372 | 373 | virtual const Sockets& registeredSockets() const; 374 | 375 | private: 376 | Sockets m_sockets; 377 | }; 378 | 379 | /* 380 | class ZMQDevice : public QObject, public QRunnable 381 | { 382 | Q_OBJECT 383 | Q_ENUMS(Type) 384 | 385 | public: 386 | enum Type 387 | { 388 | TYP_QUEUE = ZMQ_QUEUE, 389 | TYP_FORWARDED = ZMQ_FORWARDER, 390 | TYP_STREAMER = ZMQ_STREAMER 391 | }; 392 | 393 | ZMQDevice(Type type, ZMQSocket* frontend, ZMQSocket* backend); 394 | 395 | void run(); 396 | 397 | private: 398 | Type type_; 399 | ZMQSocket* frontend_; 400 | ZMQSocket* backend_; 401 | }; 402 | */ 403 | 404 | class PollingZMQContext; 405 | 406 | // An instance of this class cannot directly be created. Use one 407 | // of the 'PollingZMQContext::createSocket()' factory methods instead. 408 | class NZMQT_API PollingZMQSocket : public ZMQSocket 409 | { 410 | Q_OBJECT 411 | 412 | typedef ZMQSocket super; 413 | 414 | friend class PollingZMQContext; 415 | 416 | protected: 417 | PollingZMQSocket(PollingZMQContext* context_, Type type_); 418 | }; 419 | 420 | class NZMQT_API PollingZMQContext : public ZMQContext, public QRunnable 421 | { 422 | Q_OBJECT 423 | 424 | typedef ZMQContext super; 425 | 426 | public: 427 | PollingZMQContext(QObject* parent_ = nullptr, int io_threads_ = NZMQT_DEFAULT_IOTHREADS); 428 | 429 | // Sets the polling interval. 430 | // Note that the interval does not denote the time the zmq::poll() function will 431 | // block in order to wait for incoming messages. Instead, it denotes the time in-between 432 | // consecutive zmq::poll() calls. 433 | void setInterval(int interval_); 434 | 435 | int getInterval() const; 436 | 437 | // Starts the polling process by scheduling a call to the 'run()' method into Qt's event loop. 438 | void start() override; 439 | 440 | // Stops the polling process in the sense that no further 'run()' calls will be scheduled into 441 | // Qt's event loop. 442 | void stop() override; 443 | 444 | bool isStopped() const override; 445 | 446 | public slots: 447 | // If the polling process is not stopped (by a previous call to the 'stop()' method) this 448 | // method will call the 'poll()' method once and re-schedule a subsequent call to this method 449 | // using the current polling interval. 450 | void run(); 451 | 452 | // This method will poll on all currently available poll-items (known ZMQ sockets) 453 | // using the given timeout to wait for incoming messages. Note that this timeout has 454 | // nothing to do with the polling interval. Instead, the poll method will block the current 455 | // thread by waiting at most the specified amount of time for incoming messages. 456 | // This method is public because it can be called directly if you need to. 457 | void poll(long timeout_ = 0); 458 | 459 | signals: 460 | // This signal will be emitted by run() method if a call to poll(...) method 461 | // results in an exception. 462 | void pollError(int errorNum, const QString& errorMsg); 463 | 464 | protected: 465 | PollingZMQSocket* createSocketInternal(ZMQSocket::Type type_) override; 466 | 467 | // Add the given socket to list list of poll-items. 468 | void registerSocket(ZMQSocket* socket_) override; 469 | 470 | // Remove the given socket object from the list of poll-items. 471 | void unregisterSocket(ZMQSocket* socket_) override; 472 | 473 | private: 474 | typedef QVector PollItems; 475 | 476 | PollItems m_pollItems; 477 | QMutex m_pollItemsMutex; 478 | int m_interval; 479 | volatile bool m_stopped; 480 | }; 481 | 482 | 483 | // An instance of this class cannot directly be created. Use one 484 | // of the 'SocketNotifierZMQContext::createSocket()' factory methods instead. 485 | class NZMQT_API SocketNotifierZMQSocket : public ZMQSocket 486 | { 487 | Q_OBJECT 488 | 489 | friend class SocketNotifierZMQContext; 490 | 491 | typedef ZMQSocket super; 492 | 493 | public: 494 | using super::close; 495 | 496 | void close(); 497 | 498 | signals: 499 | // This signal will be emitted by the socket notifier callback if a call 500 | // to the events() method results in an exception. 501 | void notifierError(int errorNum, const QString& errorMsg); 502 | 503 | protected: 504 | SocketNotifierZMQSocket(ZMQContext* context_, Type type_); 505 | ~SocketNotifierZMQSocket(); 506 | 507 | protected slots: 508 | void socketReadActivity(); 509 | void socketWriteActivity(); 510 | 511 | private: 512 | QSocketNotifier *socketNotifyRead_; 513 | QSocketNotifier *socketNotifyWrite_; 514 | }; 515 | 516 | class NZMQT_API SocketNotifierZMQContext : public ZMQContext 517 | { 518 | Q_OBJECT 519 | 520 | typedef ZMQContext super; 521 | 522 | public: 523 | SocketNotifierZMQContext(QObject* parent_ = nullptr, int io_threads_ = NZMQT_DEFAULT_IOTHREADS); 524 | 525 | void start() override; 526 | 527 | void stop() override; 528 | 529 | bool isStopped() const override; 530 | 531 | signals: 532 | // This signal will be emitted by the socket notifier callback if a call 533 | // to the events() method results in an exception. 534 | void notifierError(int errorNum, const QString& errorMsg); 535 | 536 | protected: 537 | SocketNotifierZMQSocket* createSocketInternal(ZMQSocket::Type type_); 538 | }; 539 | 540 | NZMQT_API inline ZMQContext* createDefaultContext(QObject* parent_ = nullptr, int io_threads_ = NZMQT_DEFAULT_IOTHREADS) 541 | { 542 | return new NZMQT_DEFAULT_ZMQCONTEXT_IMPLEMENTATION(parent_, io_threads_); 543 | } 544 | } 545 | 546 | // Declare metatypes for using them in Qt signals. 547 | Q_DECLARE_METATYPE(QList) 548 | Q_DECLARE_METATYPE(QList< QList >) 549 | Q_DECLARE_METATYPE(nzmqt::ZMQSocket::SendFlags) 550 | 551 | 552 | #if !defined(NZMQT_LIB) 553 | #include "nzmqt/impl.hpp" 554 | #endif 555 | 556 | #endif // NZMQT_H 557 | -------------------------------------------------------------------------------- /3rdparty/cppzmq/zmq.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2011 250bpm s.r.o. 3 | Copyright (c) 2011 Botond Ballo 4 | Copyright (c) 2007-2009 iMatix Corporation 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to 8 | deal in the Software without restriction, including without limitation the 9 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | sell copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | IN THE SOFTWARE. 23 | */ 24 | 25 | #ifndef __ZMQ_HPP_INCLUDED__ 26 | #define __ZMQ_HPP_INCLUDED__ 27 | 28 | #if (__cplusplus >= 201103L) 29 | #define ZMQ_CPP11 30 | #define ZMQ_NOTHROW noexcept 31 | #define ZMQ_EXPLICIT explicit 32 | #elif (defined(_MSC_VER) && (_MSC_VER >= 1900)) 33 | #define ZMQ_CPP11 34 | #define ZMQ_NOTHROW noexcept 35 | #define ZMQ_EXPLICIT explicit 36 | #else 37 | #define ZMQ_CPP03 38 | #define ZMQ_NOTHROW 39 | #define ZMQ_EXPLICIT 40 | #endif 41 | 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #ifdef ZMQ_CPP11 53 | #include 54 | #include 55 | #include 56 | #endif 57 | 58 | // Detect whether the compiler supports C++11 rvalue references. 59 | #if (defined(__GNUC__) && (__GNUC__ > 4 || \ 60 | (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) && \ 61 | defined(__GXX_EXPERIMENTAL_CXX0X__)) 62 | #define ZMQ_HAS_RVALUE_REFS 63 | #define ZMQ_DELETED_FUNCTION = delete 64 | #elif defined(__clang__) 65 | #if __has_feature(cxx_rvalue_references) 66 | #define ZMQ_HAS_RVALUE_REFS 67 | #endif 68 | 69 | #if __has_feature(cxx_deleted_functions) 70 | #define ZMQ_DELETED_FUNCTION = delete 71 | #else 72 | #define ZMQ_DELETED_FUNCTION 73 | #endif 74 | #elif defined(_MSC_VER) && (_MSC_VER >= 1900) 75 | #define ZMQ_HAS_RVALUE_REFS 76 | #define ZMQ_DELETED_FUNCTION = delete 77 | #elif defined(_MSC_VER) && (_MSC_VER >= 1600) 78 | #define ZMQ_HAS_RVALUE_REFS 79 | #define ZMQ_DELETED_FUNCTION 80 | #else 81 | #define ZMQ_DELETED_FUNCTION 82 | #endif 83 | 84 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) 85 | #define ZMQ_NEW_MONITOR_EVENT_LAYOUT 86 | #endif 87 | 88 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) 89 | #define ZMQ_HAS_PROXY_STEERABLE 90 | /* Socket event data */ 91 | typedef struct { 92 | uint16_t event; // id of the event as bitfield 93 | int32_t value ; // value is either error code, fd or reconnect interval 94 | } zmq_event_t; 95 | #endif 96 | 97 | // Avoid using deprecated message receive function when possible 98 | #if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0) 99 | # define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags) 100 | #endif 101 | 102 | 103 | // In order to prevent unused variable warnings when building in non-debug 104 | // mode use this macro to make assertions. 105 | #ifndef NDEBUG 106 | # define ZMQ_ASSERT(expression) assert(expression) 107 | #else 108 | # define ZMQ_ASSERT(expression) (void)(expression) 109 | #endif 110 | 111 | namespace zmq 112 | { 113 | 114 | typedef zmq_free_fn free_fn; 115 | typedef zmq_pollitem_t pollitem_t; 116 | 117 | class error_t : public std::exception 118 | { 119 | public: 120 | 121 | error_t () : errnum (zmq_errno ()) {} 122 | #ifdef ZMQ_CPP11 123 | virtual const char *what () const noexcept 124 | { 125 | return zmq_strerror (errnum); 126 | } 127 | #else 128 | virtual const char *what() const throw () 129 | { 130 | return zmq_strerror(errnum); 131 | } 132 | #endif 133 | int num () const 134 | { 135 | return errnum; 136 | } 137 | 138 | private: 139 | 140 | int errnum; 141 | }; 142 | 143 | inline int poll (zmq_pollitem_t const* items_, size_t nitems_, long timeout_ = -1) 144 | { 145 | int rc = zmq_poll (const_cast(items_), static_cast(nitems_), timeout_); 146 | if (rc < 0) 147 | throw error_t (); 148 | return rc; 149 | } 150 | 151 | inline int poll(zmq_pollitem_t const* items, size_t nitems) 152 | { 153 | return poll(items, nitems, -1); 154 | } 155 | 156 | #ifdef ZMQ_CPP11 157 | inline int poll(zmq_pollitem_t const* items, size_t nitems, std::chrono::milliseconds timeout) 158 | { 159 | return poll(items, nitems, static_cast(timeout.count())); 160 | } 161 | 162 | inline int poll(std::vector const& items, std::chrono::milliseconds timeout) 163 | { 164 | return poll(items.data(), items.size(), static_cast(timeout.count())); 165 | } 166 | 167 | inline int poll(std::vector const& items, long timeout_ = -1) 168 | { 169 | return poll(items.data(), items.size(), timeout_); 170 | } 171 | #endif 172 | 173 | 174 | 175 | inline void proxy (void *frontend, void *backend, void *capture) 176 | { 177 | int rc = zmq_proxy (frontend, backend, capture); 178 | if (rc != 0) 179 | throw error_t (); 180 | } 181 | 182 | #ifdef ZMQ_HAS_PROXY_STEERABLE 183 | inline void proxy_steerable (void *frontend, void *backend, void *capture, void *control) 184 | { 185 | int rc = zmq_proxy_steerable (frontend, backend, capture, control); 186 | if (rc != 0) 187 | throw error_t (); 188 | } 189 | #endif 190 | 191 | inline void version (int *major_, int *minor_, int *patch_) 192 | { 193 | zmq_version (major_, minor_, patch_); 194 | } 195 | 196 | #ifdef ZMQ_CPP11 197 | inline std::tuple version() 198 | { 199 | std::tuple v; 200 | zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v) ); 201 | return v; 202 | } 203 | #endif 204 | 205 | class message_t 206 | { 207 | friend class socket_t; 208 | 209 | public: 210 | 211 | inline message_t () 212 | { 213 | int rc = zmq_msg_init (&msg); 214 | if (rc != 0) 215 | throw error_t (); 216 | } 217 | 218 | inline explicit message_t (size_t size_) 219 | { 220 | int rc = zmq_msg_init_size (&msg, size_); 221 | if (rc != 0) 222 | throw error_t (); 223 | } 224 | 225 | template message_t(I first, I last): 226 | msg() 227 | { 228 | typedef typename std::iterator_traits::difference_type size_type; 229 | typedef typename std::iterator_traits::value_type value_t; 230 | 231 | size_type const size_ = std::distance(first, last)*sizeof(value_t); 232 | int const rc = zmq_msg_init_size (&msg, size_); 233 | if (rc != 0) 234 | throw error_t (); 235 | value_t* dest = data(); 236 | while (first != last) 237 | { 238 | *dest = *first; 239 | ++dest; ++first; 240 | } 241 | } 242 | 243 | inline message_t (const void *data_, size_t size_) 244 | { 245 | int rc = zmq_msg_init_size (&msg, size_); 246 | if (rc != 0) 247 | throw error_t (); 248 | memcpy(data(), data_, size_); 249 | } 250 | 251 | inline message_t (void *data_, size_t size_, free_fn *ffn_, 252 | void *hint_ = NULL) 253 | { 254 | int rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); 255 | if (rc != 0) 256 | throw error_t (); 257 | } 258 | 259 | #ifdef ZMQ_HAS_RVALUE_REFS 260 | inline message_t (message_t &&rhs): msg (rhs.msg) 261 | { 262 | int rc = zmq_msg_init (&rhs.msg); 263 | if (rc != 0) 264 | throw error_t (); 265 | } 266 | 267 | inline message_t &operator = (message_t &&rhs) ZMQ_NOTHROW 268 | { 269 | std::swap (msg, rhs.msg); 270 | return *this; 271 | } 272 | #endif 273 | 274 | inline ~message_t () ZMQ_NOTHROW 275 | { 276 | int rc = zmq_msg_close (&msg); 277 | ZMQ_ASSERT (rc == 0); 278 | } 279 | 280 | inline void rebuild () 281 | { 282 | int rc = zmq_msg_close (&msg); 283 | if (rc != 0) 284 | throw error_t (); 285 | rc = zmq_msg_init (&msg); 286 | if (rc != 0) 287 | throw error_t (); 288 | } 289 | 290 | inline void rebuild (size_t size_) 291 | { 292 | int rc = zmq_msg_close (&msg); 293 | if (rc != 0) 294 | throw error_t (); 295 | rc = zmq_msg_init_size (&msg, size_); 296 | if (rc != 0) 297 | throw error_t (); 298 | } 299 | 300 | inline void rebuild (const void *data_, size_t size_) 301 | { 302 | int rc = zmq_msg_close (&msg); 303 | if (rc != 0) 304 | throw error_t (); 305 | rc = zmq_msg_init_size (&msg, size_); 306 | if (rc != 0) 307 | throw error_t (); 308 | memcpy(data(), data_, size_); 309 | } 310 | 311 | inline void rebuild (void *data_, size_t size_, free_fn *ffn_, 312 | void *hint_ = NULL) 313 | { 314 | int rc = zmq_msg_close (&msg); 315 | if (rc != 0) 316 | throw error_t (); 317 | rc = zmq_msg_init_data (&msg, data_, size_, ffn_, hint_); 318 | if (rc != 0) 319 | throw error_t (); 320 | } 321 | 322 | inline void move (message_t const *msg_) 323 | { 324 | int rc = zmq_msg_move (&msg, const_cast(&(msg_->msg))); 325 | if (rc != 0) 326 | throw error_t (); 327 | } 328 | 329 | inline void copy (message_t const *msg_) 330 | { 331 | int rc = zmq_msg_copy (&msg, const_cast(&(msg_->msg))); 332 | if (rc != 0) 333 | throw error_t (); 334 | } 335 | 336 | inline bool more () const ZMQ_NOTHROW 337 | { 338 | int rc = zmq_msg_more (const_cast(&msg) ); 339 | return rc != 0; 340 | } 341 | 342 | inline void *data () ZMQ_NOTHROW 343 | { 344 | return zmq_msg_data (&msg); 345 | } 346 | 347 | inline const void* data () const ZMQ_NOTHROW 348 | { 349 | return zmq_msg_data (const_cast(&msg)); 350 | } 351 | 352 | inline size_t size () const ZMQ_NOTHROW 353 | { 354 | return zmq_msg_size (const_cast(&msg)); 355 | } 356 | 357 | template T* data() ZMQ_NOTHROW 358 | { 359 | return static_cast( data() ); 360 | } 361 | 362 | template T const* data() const ZMQ_NOTHROW 363 | { 364 | return static_cast( data() ); 365 | } 366 | 367 | inline bool equal(const message_t* other) const ZMQ_NOTHROW 368 | { 369 | if (size() != other->size()) 370 | return false; 371 | std::string a(data(), size()); 372 | std::string b(other->data(), other->size()); 373 | return a == b; 374 | } 375 | 376 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) 377 | inline const char* gets(const char *property_) 378 | { 379 | const char* value = zmq_msg_gets (&msg, property_); 380 | if (value == NULL) 381 | throw error_t (); 382 | return value; 383 | } 384 | #endif 385 | 386 | private: 387 | // The underlying message 388 | zmq_msg_t msg; 389 | 390 | // Disable implicit message copying, so that users won't use shared 391 | // messages (less efficient) without being aware of the fact. 392 | message_t (const message_t&) ZMQ_DELETED_FUNCTION; 393 | void operator = (const message_t&) ZMQ_DELETED_FUNCTION; 394 | }; 395 | 396 | class context_t 397 | { 398 | friend class socket_t; 399 | 400 | public: 401 | inline context_t () 402 | { 403 | ptr = zmq_ctx_new (); 404 | if (ptr == NULL) 405 | throw error_t (); 406 | } 407 | 408 | 409 | inline explicit context_t (int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT) 410 | { 411 | ptr = zmq_ctx_new (); 412 | if (ptr == NULL) 413 | throw error_t (); 414 | 415 | int rc = zmq_ctx_set (ptr, ZMQ_IO_THREADS, io_threads_); 416 | ZMQ_ASSERT (rc == 0); 417 | 418 | rc = zmq_ctx_set (ptr, ZMQ_MAX_SOCKETS, max_sockets_); 419 | ZMQ_ASSERT (rc == 0); 420 | } 421 | 422 | #ifdef ZMQ_HAS_RVALUE_REFS 423 | inline context_t (context_t &&rhs) ZMQ_NOTHROW : ptr (rhs.ptr) 424 | { 425 | rhs.ptr = NULL; 426 | } 427 | inline context_t &operator = (context_t &&rhs) ZMQ_NOTHROW 428 | { 429 | std::swap (ptr, rhs.ptr); 430 | return *this; 431 | } 432 | #endif 433 | 434 | inline ~context_t () ZMQ_NOTHROW 435 | { 436 | close(); 437 | } 438 | 439 | inline void close() ZMQ_NOTHROW 440 | { 441 | if (ptr == NULL) 442 | return; 443 | 444 | int rc = zmq_ctx_destroy (ptr); 445 | ZMQ_ASSERT (rc == 0); 446 | ptr = NULL; 447 | } 448 | 449 | // Be careful with this, it's probably only useful for 450 | // using the C api together with an existing C++ api. 451 | // Normally you should never need to use this. 452 | inline ZMQ_EXPLICIT operator void* () ZMQ_NOTHROW 453 | { 454 | return ptr; 455 | } 456 | 457 | inline ZMQ_EXPLICIT operator void const* () const ZMQ_NOTHROW 458 | { 459 | return ptr; 460 | } 461 | private: 462 | 463 | void *ptr; 464 | 465 | context_t (const context_t&) ZMQ_DELETED_FUNCTION; 466 | void operator = (const context_t&) ZMQ_DELETED_FUNCTION; 467 | }; 468 | 469 | #ifdef ZMQ_CPP11 470 | enum class socket_type: int 471 | { 472 | req = ZMQ_REQ, 473 | rep = ZMQ_REP, 474 | dealer = ZMQ_DEALER, 475 | router = ZMQ_ROUTER, 476 | pub = ZMQ_PUB, 477 | sub = ZMQ_SUB, 478 | xpub = ZMQ_XPUB, 479 | xsub = ZMQ_XSUB, 480 | push = ZMQ_PUSH, 481 | pull = ZMQ_PULL, 482 | #if ZMQ_VERSION_MAJOR < 4 483 | pair = ZMQ_PAIR 484 | #else 485 | pair = ZMQ_PAIR, 486 | stream = ZMQ_STREAM 487 | #endif 488 | }; 489 | #endif 490 | 491 | class socket_t 492 | { 493 | friend class monitor_t; 494 | friend class poller_t; 495 | public: 496 | inline socket_t(context_t& context_, int type_) 497 | { 498 | init(context_, type_); 499 | } 500 | 501 | #ifdef ZMQ_CPP11 502 | inline socket_t(context_t& context_, socket_type type_) 503 | { 504 | init(context_, static_cast(type_)); 505 | } 506 | #endif 507 | 508 | #ifdef ZMQ_HAS_RVALUE_REFS 509 | inline socket_t(socket_t&& rhs) ZMQ_NOTHROW : ptr(rhs.ptr) 510 | { 511 | rhs.ptr = NULL; 512 | } 513 | inline socket_t& operator=(socket_t&& rhs) ZMQ_NOTHROW 514 | { 515 | std::swap(ptr, rhs.ptr); 516 | return *this; 517 | } 518 | #endif 519 | 520 | inline ~socket_t () ZMQ_NOTHROW 521 | { 522 | close(); 523 | } 524 | 525 | inline operator void* () ZMQ_NOTHROW 526 | { 527 | return ptr; 528 | } 529 | 530 | inline operator void const* () const ZMQ_NOTHROW 531 | { 532 | return ptr; 533 | } 534 | 535 | inline void close() ZMQ_NOTHROW 536 | { 537 | if(ptr == NULL) 538 | // already closed 539 | return ; 540 | int rc = zmq_close (ptr); 541 | ZMQ_ASSERT (rc == 0); 542 | ptr = 0 ; 543 | } 544 | 545 | template void setsockopt(int option_, T const& optval) 546 | { 547 | setsockopt(option_, &optval, sizeof(T) ); 548 | } 549 | 550 | inline void setsockopt (int option_, const void *optval_, 551 | size_t optvallen_) 552 | { 553 | int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_); 554 | if (rc != 0) 555 | throw error_t (); 556 | } 557 | 558 | inline void getsockopt (int option_, void *optval_, 559 | size_t *optvallen_) const 560 | { 561 | int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_); 562 | if (rc != 0) 563 | throw error_t (); 564 | } 565 | 566 | template T getsockopt(int option_) const 567 | { 568 | T optval; 569 | size_t optlen = sizeof(T); 570 | getsockopt(option_, &optval, &optlen ); 571 | return optval; 572 | } 573 | 574 | inline void bind(std::string const& addr) 575 | { 576 | bind(addr.c_str()); 577 | } 578 | 579 | inline void bind (const char *addr_) 580 | { 581 | int rc = zmq_bind (ptr, addr_); 582 | if (rc != 0) 583 | throw error_t (); 584 | } 585 | 586 | inline void unbind(std::string const& addr) 587 | { 588 | unbind(addr.c_str()); 589 | } 590 | 591 | inline void unbind (const char *addr_) 592 | { 593 | int rc = zmq_unbind (ptr, addr_); 594 | if (rc != 0) 595 | throw error_t (); 596 | } 597 | 598 | inline void connect(std::string const& addr) 599 | { 600 | connect(addr.c_str()); 601 | } 602 | 603 | inline void connect (const char *addr_) 604 | { 605 | int rc = zmq_connect (ptr, addr_); 606 | if (rc != 0) 607 | throw error_t (); 608 | } 609 | 610 | inline void disconnect(std::string const& addr) 611 | { 612 | disconnect(addr.c_str()); 613 | } 614 | 615 | inline void disconnect (const char *addr_) 616 | { 617 | int rc = zmq_disconnect (ptr, addr_); 618 | if (rc != 0) 619 | throw error_t (); 620 | } 621 | 622 | inline bool connected() const ZMQ_NOTHROW 623 | { 624 | return(ptr != NULL); 625 | } 626 | 627 | inline size_t send (const void *buf_, size_t len_, int flags_ = 0) 628 | { 629 | int nbytes = zmq_send (ptr, buf_, len_, flags_); 630 | if (nbytes >= 0) 631 | return (size_t) nbytes; 632 | if (zmq_errno () == EAGAIN) 633 | return 0; 634 | throw error_t (); 635 | } 636 | 637 | inline bool send (message_t &msg_, int flags_ = 0) 638 | { 639 | int nbytes = zmq_msg_send (&(msg_.msg), ptr, flags_); 640 | if (nbytes >= 0) 641 | return true; 642 | if (zmq_errno () == EAGAIN) 643 | return false; 644 | throw error_t (); 645 | } 646 | 647 | template bool send(I first, I last, int flags_=0) 648 | { 649 | zmq::message_t msg(first, last); 650 | return send(msg, flags_); 651 | } 652 | 653 | #ifdef ZMQ_HAS_RVALUE_REFS 654 | inline bool send (message_t &&msg_, int flags_ = 0) 655 | { 656 | return send(msg_, flags_); 657 | } 658 | #endif 659 | 660 | inline size_t recv (void *buf_, size_t len_, int flags_ = 0) 661 | { 662 | int nbytes = zmq_recv (ptr, buf_, len_, flags_); 663 | if (nbytes >= 0) 664 | return (size_t) nbytes; 665 | if (zmq_errno () == EAGAIN) 666 | return 0; 667 | throw error_t (); 668 | } 669 | 670 | inline bool recv (message_t *msg_, int flags_ = 0) 671 | { 672 | int nbytes = zmq_msg_recv (&(msg_->msg), ptr, flags_); 673 | if (nbytes >= 0) 674 | return true; 675 | if (zmq_errno () == EAGAIN) 676 | return false; 677 | throw error_t (); 678 | } 679 | 680 | private: 681 | inline void init(context_t& context_, int type_) 682 | { 683 | ctxptr = context_.ptr; 684 | ptr = zmq_socket (context_.ptr, type_ ); 685 | if (ptr == NULL) 686 | throw error_t (); 687 | } 688 | 689 | void *ptr; 690 | void *ctxptr; 691 | 692 | socket_t (const socket_t&) ZMQ_DELETED_FUNCTION; 693 | void operator = (const socket_t&) ZMQ_DELETED_FUNCTION; 694 | }; 695 | 696 | class monitor_t 697 | { 698 | public: 699 | monitor_t() : socketPtr(NULL) {} 700 | virtual ~monitor_t() {} 701 | 702 | void monitor(socket_t &socket, std::string const& addr, int events = ZMQ_EVENT_ALL) 703 | { 704 | monitor(socket, addr.c_str(), events); 705 | } 706 | 707 | void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) 708 | { 709 | int rc = zmq_socket_monitor(socket.ptr, addr_, events); 710 | if (rc != 0) 711 | throw error_t (); 712 | 713 | socketPtr = socket.ptr; 714 | void *s = zmq_socket (socket.ctxptr, ZMQ_PAIR); 715 | assert (s); 716 | 717 | rc = zmq_connect (s, addr_); 718 | assert (rc == 0); 719 | 720 | on_monitor_started(); 721 | 722 | while (true) { 723 | zmq_msg_t eventMsg; 724 | zmq_msg_init (&eventMsg); 725 | rc = zmq_msg_recv (&eventMsg, s, 0); 726 | if (rc == -1 && zmq_errno() == ETERM) 727 | break; 728 | assert (rc != -1); 729 | #if ZMQ_VERSION_MAJOR >= 4 730 | const char* data = static_cast(zmq_msg_data(&eventMsg)); 731 | zmq_event_t msgEvent; 732 | memcpy(&msgEvent.event, data, sizeof(uint16_t)); data += sizeof(uint16_t); 733 | memcpy(&msgEvent.value, data, sizeof(int32_t)); 734 | zmq_event_t* event = &msgEvent; 735 | #else 736 | zmq_event_t* event = static_cast(zmq_msg_data(&eventMsg)); 737 | #endif 738 | 739 | #ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT 740 | zmq_msg_t addrMsg; 741 | zmq_msg_init (&addrMsg); 742 | rc = zmq_msg_recv (&addrMsg, s, 0); 743 | if (rc == -1 && zmq_errno() == ETERM) 744 | break; 745 | assert (rc != -1); 746 | const char* str = static_cast(zmq_msg_data (&addrMsg)); 747 | std::string address(str, str + zmq_msg_size(&addrMsg)); 748 | zmq_msg_close (&addrMsg); 749 | #else 750 | // 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. 751 | std::string address = event->data.connected.addr; 752 | #endif 753 | 754 | #ifdef ZMQ_EVENT_MONITOR_STOPPED 755 | if (event->event == ZMQ_EVENT_MONITOR_STOPPED) 756 | break; 757 | #endif 758 | 759 | switch (event->event) { 760 | case ZMQ_EVENT_CONNECTED: 761 | on_event_connected(*event, address.c_str()); 762 | break; 763 | case ZMQ_EVENT_CONNECT_DELAYED: 764 | on_event_connect_delayed(*event, address.c_str()); 765 | break; 766 | case ZMQ_EVENT_CONNECT_RETRIED: 767 | on_event_connect_retried(*event, address.c_str()); 768 | break; 769 | case ZMQ_EVENT_LISTENING: 770 | on_event_listening(*event, address.c_str()); 771 | break; 772 | case ZMQ_EVENT_BIND_FAILED: 773 | on_event_bind_failed(*event, address.c_str()); 774 | break; 775 | case ZMQ_EVENT_ACCEPTED: 776 | on_event_accepted(*event, address.c_str()); 777 | break; 778 | case ZMQ_EVENT_ACCEPT_FAILED: 779 | on_event_accept_failed(*event, address.c_str()); 780 | break; 781 | case ZMQ_EVENT_CLOSED: 782 | on_event_closed(*event, address.c_str()); 783 | break; 784 | case ZMQ_EVENT_CLOSE_FAILED: 785 | on_event_close_failed(*event, address.c_str()); 786 | break; 787 | case ZMQ_EVENT_DISCONNECTED: 788 | on_event_disconnected(*event, address.c_str()); 789 | break; 790 | #ifdef ZMQ_BUILD_DRAFT_API 791 | case ZMQ_EVENT_HANDSHAKE_FAILED: 792 | on_event_handshake_failed(*event, address.c_str()); 793 | break; 794 | case ZMQ_EVENT_HANDSHAKE_SUCCEED: 795 | on_event_handshake_succeed(*event, address.c_str()); 796 | break; 797 | #endif 798 | default: 799 | on_event_unknown(*event, address.c_str()); 800 | break; 801 | } 802 | zmq_msg_close (&eventMsg); 803 | } 804 | zmq_close (s); 805 | socketPtr = NULL; 806 | } 807 | 808 | #ifdef ZMQ_EVENT_MONITOR_STOPPED 809 | void abort() 810 | { 811 | if (socketPtr) 812 | zmq_socket_monitor(socketPtr, NULL, 0); 813 | } 814 | #endif 815 | virtual void on_monitor_started() {} 816 | virtual void on_event_connected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 817 | virtual void on_event_connect_delayed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 818 | virtual void on_event_connect_retried(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 819 | virtual void on_event_listening(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 820 | virtual void on_event_bind_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 821 | virtual void on_event_accepted(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 822 | virtual void on_event_accept_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 823 | virtual void on_event_closed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 824 | virtual void on_event_close_failed(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 825 | virtual void on_event_disconnected(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 826 | virtual void on_event_handshake_failed(const zmq_event_t &event_, const char* addr_) { (void) event_; (void) addr_; } 827 | virtual void on_event_handshake_succeed(const zmq_event_t &event_, const char* addr_) { (void) event_; (void) addr_; } 828 | virtual void on_event_unknown(const zmq_event_t &event_, const char* addr_) { (void)event_; (void)addr_; } 829 | private: 830 | void* socketPtr; 831 | }; 832 | 833 | #if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) 834 | class poller_t 835 | { 836 | public: 837 | poller_t () : poller_ptr (zmq_poller_new ()) 838 | { 839 | if (!poller_ptr) 840 | throw error_t (); 841 | } 842 | 843 | ~poller_t () 844 | { 845 | zmq_poller_destroy (&poller_ptr); 846 | } 847 | 848 | bool add (zmq::socket_t &socket, short events, std::function &handler) 849 | { 850 | if (0 == zmq_poller_add (poller_ptr, socket.ptr, handler ? &handler : NULL, events)) { 851 | poller_events.emplace_back (zmq_poller_event_t ()); 852 | return true; 853 | } 854 | return false; 855 | } 856 | 857 | bool remove (zmq::socket_t &socket) 858 | { 859 | if (0 == zmq_poller_remove (poller_ptr, socket.ptr)) { 860 | poller_events.pop_back (); 861 | return true; 862 | } 863 | return false; 864 | } 865 | 866 | bool wait (std::chrono::milliseconds timeout) 867 | { 868 | int rc = zmq_poller_wait_all (poller_ptr, poller_events.data (), poller_events.size (), static_cast(timeout.count ())); 869 | if (rc >= 0) { 870 | std::for_each (poller_events.begin (), poller_events.begin () + rc, [](zmq_poller_event_t& event) { 871 | if (event.user_data != NULL) 872 | (*reinterpret_cast*> (event.user_data)) (); 873 | }); 874 | return true; 875 | } 876 | 877 | if (zmq_errno () == ETIMEDOUT) 878 | return false; 879 | 880 | throw error_t (); 881 | } 882 | 883 | private: 884 | void *poller_ptr; 885 | std::vector poller_events; 886 | }; 887 | #endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) 888 | 889 | } 890 | 891 | #endif 892 | --------------------------------------------------------------------------------