Overview

This tutorial demonstrates how Mongoose Library can be used to implement a simple MQTT 3.1.1 server that is capable of:

  1. Handling CONNECT requests from clients. User / password are not verified
  2. Handling SUBSCRIBE requests
  3. 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

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 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.

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. First, build and start it. Assuming we're working on Mac or Linux workstation, start a terminal, and execute main:

$ 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: 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