NNG, like its predecessors nanomsg (and to some extent ZeroMQ), is a lightweight, broker-less library, offering a simple API to solve common recurring messaging problems, such as publish/subscribe, RPC-style request/reply, or service discovery. – nng

NNGPP, C++ wrapper around the nanomsg NNG API. – nngpp
Instead of Kafaka and DDS, NNG is broker-less and less compleicated but full featured with messaging patterns such as publish/subscribe, RPC-style request/reply, or service discovery.


Using NNG with NNGPP

Actually gpt is more good at demostrating usage cases of nng, but notice that nngpp’s last commit is 6 years ago, gpt can’t tell the right usage case of nngpp in my case and there are few problems with this old repository.

Let’s go straight, when using nngpp for sub/pub case, code like this:

sub.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <nngpp/nngpp.h>
#include <nngpp/protocol/sub0.h>
#include <string>

int main (int argc, char *argv[]) {
nng::socket sub_sock = nng::sub::open();

nng::sub::set_opt_subscribe(sub_sock, "");

sub_sock.dial("tcp://127.0.0.1:5000");

while (true) {
auto msg = sub_sock.recv_msg();
std::string s(
reinterpret_cast<char*>(msg.body().data()),
msg.body().size()
);
std::cout << "recv: " << s << std::endl;
}

return 0;
}

pub.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <chrono>
#include <nngpp/nngpp.h>
#include <nngpp/protocol/pub0.h>
#include <string>
#include <thread>
#include <iostream>

int main (int argc, char *argv[]) {

nng::socket pub = nng::pub::open();
pub.listen("tcp://127.0.0.1:5000");

int i = 0;
while (true) {
pub.send(nng::view("ping", 4));
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
return 0;
}

The SUB can’t receive the message from the PUB even if the SUB subscribes all topics by set_opt_subscribe(sub_sock, "");, and when I review the source of nngpp, everything looks ok.

Then I tried using bearly nng for sub/pub, it works can’t be more right. So it’s clearly that there is a problem with nngpp. It’s reasonalbe since it’s last commit has been so long.

Digging into code with gdb, finally caught the bug: when calling set_opt_subscribe in nngpp, it finally calls the nng api nng_socket_set, but pass the wrong parameter “topic string size”.

set_opt_subscribe:

1
2
3
inline void set_opt_subscribe( socket_view s, view v ) {
s.set_opt( to_name(option::subscribe), v );
}

set_opt:

1
2
3
4
5
6
void set_opt( const char* name, view v ) const {
int r = nng_socket_set(s,name,v.data(),v.size());
if( r != 0 ) {
throw exception(r,"nng_socket_set");
}
}

Check the code above, it pass the v.size() as the parameter “topic string size”. When the string is "", the size should be 0.

But in fact the v.size()(nngpp’s custom class view for data manage) returns 1 while v.data() is "", since it allocated buffer for \0.

I’m not sure if it’s because of the update of nng that make nngpp doesn’t work as expected any more, or the sub/pub is never tested in nngpp, but it’s indeed a bug which make pub/sub in nngpp doesn’t work any more.

Here is the correct version of set_opt:

1
2
3
4
5
6
void set_opt( const char* name, view v ) const {
int r = nng_socket_set(s,name,v.data(),v.size() - 1);
if( r != 0 ) {
throw exception(r,"nng_socket_set");
}
}

The end

I may fork nngpp and maintain it as long as I am using it for my project.

Transport types supported by nng, awesome!

Transport Scope Speed Secure Typical Use
inproc:// same process ⭐⭐⭐⭐⭐ threads
ipc:// same host ⭐⭐⭐⭐ OS perms local IPC
tcp:// network ⭐⭐⭐ simple network
tls+tcp:// network ⭐⭐ secure backend
ws:// network ⭐⭐ browser
wss:// network ⭐⭐ browser secure
quic:// network ⭐⭐⭐⭐ modern low-latency

Enjoy!


See also


References