MQTT server
Overview
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/tutorials/mqtt/mqtt-server
Global variables
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:
Signal handler
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
The 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:
Event handler
The event handler function is the meat of the server implementation.
It handles two events: MG_EV_CLOSE
and MG_EV_MQTT_CMD
.
The code that handles MG_EV_MQTT_CMD
looks up which specific command
has been sent:
Handling MQTT CONNECT
We 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 PUBLISH 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.
Handling disconnection
When a client disconnects, we iterate over the global subscription list, and remove all subscriptions installed by that client.
Testing
Let's test our server. Follow the Build Tools tutorial to setup your development environment. Start a terminal in the project directory; clone the Mongoose Library repo, and execute:
make clean all
Make sure you have mosquitto client setup on your workstartion. Start another
terminal and subscribe to three topics: a/#
, b/+/test
and c/foo
~$ 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 b/123/test
:
$ mosquitto_pub -h localhost -p 1883 -t b/123/test -m hello
And of course on topic a/what/ever
:
$ mosquitto_pub -h localhost -p 1883 -t a/what/ever -m hello