This tutorial demonstrates how Mongoose Library can be used to implement a simple MQTT 3.1.1 server that is capable of:
- Handling CONNECT requests from clients. User / password are not verified
- Handling SUBSCRIBE requests
- Handling PUBLISH request
This implementation, therefore, implements only a subset of MQTT specification, but it is enough for preforming some basic tasks, and is easy to extend.
A full source code for this tutorial is at https://github.com/cesanta/mongoose/tree/master/examples/mqtt-server
First we declare global variables to make it easy to change tunable parameters. In our case it is one - a listening address:
Also, we need to keep a list of subscriptions. For each SUBSCRIBE request, we'd like to store which connection requested it, which QoS, and which topic:
It is a best practice to register a signal handler. Our signal handler function just sets a global variable which will be checked in the event loop:
main() function is usual - we initialise an event manager,
create a listening connection, and fall into an event loop until a termination
signal is received. Then we cleanup and exit:
The event handler function is the meat of the server implementation.
It handles two events:
The code that handles
MG_EV_MQTT_CMD looks up which specific command
has been sent:
Handling MQTT CONNECTWe perform some sanity check for the received data, check that the client is using MQTT version 3, and send a successful CONNACK response back.
A CONNECT command sent by a client may contain extra settings - like user, password, will message, "clean session" flag, client ID, and others. We silently ignore them.
Handling MQTT SUBSCRIBE
When SUBSCRIBE command is received, it may contain multiple topic names with
QoS for each. We iterate over all topics using
mg_mqtt_next_sub API function, and
for each requested topic, create a subscription descriptor and add it to the
global list of subscription,
s_subs. Next we send a SUBACK acknowledgement
with a number of topics we've handled.
Handling MQTT PUBLISH
Hadling PUBSLISH requests is easy - we iterate a list of subscriptions, compare topic names, and send the message to the subscribed connection if topics match. We use mg_globmatch() as a matching function, which handles wildcards.
When a client disconnects, we iterate over the global subscription list, and remove all subscriptions installed by that client.
Let's test our server. First, build and start it. Assuming we're working
on Mac or Linux workstation, start a terminal, and execute
$ make cc ../../mongoose.c -I../.. -W -Wall -DMG_ENABLE_LINES=1 -o example main.c ./example 2021-11-11 11:53:59 2 main.c:107:main Starting on mqtt://0.0.0.0:1883
Make sure you have mosquitto client setup on your workstartion. Start another
terminal and subscribe to three topics:
~$ mosquitto_sub -v -h localhost -p 1883 -t 'a/#' -t 'b/+/test' -t c/foo
Start another terminal. Let's publish messages! First, send a message
to a topic
d/. We are not subscribed to that topic so should not see
anything in the "subscribed" terminal:
$ mosquitto_pub -h localhost -p 1883 -t d/ -m hello
Now let's send to a topic
c/foo, which we should catch:
$ mosquitto_pub -h localhost -p 1883 -t c/foo -m hello
Also, we should catch a message on topic
$ mosquitto_pub -h localhost -p 1883 -t b/123/test -m hello
And of course on topic
$ mosquitto_pub -h localhost -p 1883 -t a/what/ever -m hello