Mongoose Docs https://www.cesanta.com/docs/#send-and-receive-buffers
User Guide#
Introduction#
Mongoose is a networking library for C/C++. It implements an event-driven, non-blocking APIs for TCP, UDP, HTTP, WebSocket, MQTT. It has been designed for connecting devices and bringing them online. On the market since 2004, used by vast number of open source and commercial products - it even runs on the International Space Station! Mongoose makes embedded network programming fast, robust, and easy.
Features#
- Cross-platform: works on Linux/UNIX, MacOS, QNX, eCos, Windows, Android, iPhone, FreeRTOS, etc
- Supported hardware platforms: TI CC3200, TI MSP432, NRF52, STM32, PIC32, ESP8266, ESP32 and more
- Builtin protocols:
- plain TCP, plain UDP, SSL/TLS (over TCP, one-way or two-way)
- HTTP client, HTTP server
- WebSocket client, WebSocket server
- MQTT client
- DNS client, async DNS resolver
- Single-threaded, asynchronous, non-blocking core with simple event-based API
- Native support for LWIP embedded TCP/IP stack
- Tiny static and run-time footprint
- Source code is both ISO C and ISO C++ compliant
- Very easy to integrate: just copy mongoose.c and mongoose.h files to your build tree
Concept#
Mongoose has three basic data structures:
struct mg_mgr
- an event manager that holds all active connectionsstruct mg_connection
- describes a connectionstruct mg_iobuf
- describes data buffer (received or sent data)
Connections could be either listening, outbound or inbound. Outbound connections are created by the mg_connect()
call. Listening connections are created by the mg_listen()
call. Inbound connections are those accepted by a listening connection. Each connection is described by a struct mg_connection
structure, which has a number of fields. All fields are exposed to the application by design, to give an application a full visibility into the Mongoose's internals.
An application that uses mongoose should follow a standard pattern of event-driven application:
-
Declare and initialise an event manager:
struct mg_mgr mgr; mg_mgr_init(&mgr);
-
Create connections. For example, a server application should create listening connections. When any connection is created (listening or outgoing), an event handler function must be specified. An event handler function defines connection's behavior.
struct mg_connection *c = mg_http_listen(&mgr, "0.0.0.0:8000", fn, arg);
-
Create an event loop by calling
mg_mgr_poll()
:for (;;) { mg_mgr_poll(&mgr, 1000); }
mg_mgr_poll()
iterates over all sockets, accepts new connections, sends and receives data, closes connections and calls event handler functions for the respective events.
Since the Mongoose's core is not protected against concurrent accesses, make sure that all mg_*
API functions are called from the same thread or RTOS task.
Send and receive buffers#
Each connection has a send and receive buffer:
struct mg_connection::send
- data to be sent to a peerstruct mg_connection::recv
- data received from a peer
When data arrives, Mongoose appends received data to the recv
and triggers an MG_EV_RECV
event. The user may send data back by calling one of the output functions, like mg_send()
or mg_printf()
. Output functions append data to the send
buffer. When Mongoose successfully writes data to the socket, it discards data from struct mg_connection::send
and sends an MG_EV_SEND
event.
Event handler function#
Each connection has an event handler function associated with it. That function must be implemented by the user. Event handler is the key element of the Mongoose application, since it defines the connection's behaviour. This is what an event handler function looks like:
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
switch (ev) {
/* Event handler code that defines behavior of the connection */
...
}
}
struct mg_connection *c
- a connection that received an eventint ev
- an event number, defined in mongoose.h. For example, when data arrives on an inbound connection, ev would beMG_EV_RECV
void *ev_data
- points to the event-specific data, and it has a different meaning for different events. For example, for anMG_EV_RECV
event,ev_data
is anint *
pointing to the number of bytes received from a remote peer and saved into thec->recv
IO buffer. The exact meaning ofev_data
is described for each event. Protocol-specific events usually haveev_data
pointing to structures that hold protocol-specific informationvoid *fn_data
- a user-defined pointer for the connection, which is a placeholder for application-specific data
Events#
Below is the list of events trigged by Mongoose, taken as-is from mongoose.h
. For each event, a comment describes a meaning of the ev_data
pointer passed to an event handler:
enum {
MG_EV_ERROR, // Error char *error_message
MG_EV_POLL, // mg_mgr_poll iteration unsigned long *millis
MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_READ, // Data received from socket struct mg_str *
MG_EV_WRITE, // Data written to socket int *num_bytes_written
MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_MSG, // HTTP request/response struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received struct timeval *
MG_EV_USER, // Starting ID for user events
};
Connection flags#
struct mg_connection
has a bitfield with connection flags. Flags are binary, they can be either 0 or 1. Some flags are set by Mongoose and must be not changed by an application code, for example is_udp
flag tells application if that connection is UDP or not. Some flags can be changed by application, for example, is_drainig
flag, if set by an application, tells Mongoose to send the remaining data to peer, and when everything is sent, close the connection.
User-changeable flags are: is_hexdumping
, is_drainig
, is_closing
.
This is taken from mongoose.h
as-is:
struct mg_connection {
...
unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection
unsigned is_accepted : 1; // Accepted (server) connection
unsigned is_resolving : 1; // Non-blocking DNS resolv is in progress
unsigned is_connecting : 1; // Non-blocking connect is in progress
unsigned is_tls : 1; // TLS-enabled connection
unsigned is_tls_hs : 1; // TLS handshake is in progress
unsigned is_udp : 1; // UDP connection
unsigned is_websocket : 1; // WebSocket connection
unsigned is_hexdumping : 1; // Hexdump in/out traffic
unsigned is_draining : 1; // Send remaining data, then close and free
unsigned is_closing : 1; // Close and free the connection immediately
unsigned is_readable : 1; // Connection is ready to read
unsigned is_writable : 1; // Connection is ready to write
};
Build options#
Mongoose source code ships in two files:
- mongoose.h - API definitions
- mongoose.c - implementation
Therefore to integrate Mongoose into an application, simply copy these two files to the application's source tree.
The mongoose.c
and mongoose.h
files are, actually, an amalgamation - a non-amalgamated sources can be found at https://github.com/cesanta/mongoose/tree/master/src
Mongoose source code uses a bunch of build constants defined at https://github.com/cesanta/mongoose/blob/master/src/config.h, together with their default values.
In order to change the constant during build time, use the -D <PREPROCESSOR_FLAG>
compiler option. For example, to disable both MQTT, compile the application my_app.c
like this (assumed UNIX system):
$ cc my_app.c mongoose.c -D MG_MQTT_ENABLE=0
Here is a list of build constants and their default values:
Name | Default | Description |
---|---|---|
MG_ENABLE_LWIP |
0 | Use LWIP low-level API instead of BSD sockets |
MG_ENABLE_SOCKET |
1 | Use BSD socket low-level API |
MG_ENABLE_MBEDTLS |
0 | Enable Mbed TLS library |
MG_ENABLE_OPENSSL |
0 | Enable OpenSSL library |
MG_ENABLE_FS |
1 | Enable API that use filesystem, like mg_http_send_file() |
MG_ENABLE_IPV6 |
0 | Enable IPv6 |
MG_ENABLE_LOG |
1 | Enable LOG() macro |
MG_ENABLE_MD5 |
0 | Use native MD5 implementation |
MG_ENABLE_DIRECTORY_LISTING |
0 | Enable directory listing for HTTP server |
MG_ENABLE_HTTP_DEBUG_ENDPOINT |
0 | Enable /debug/info debug URI |
MG_ENABLE_SOCKETPAIR |
0 | Enable mg_socketpair() for multi-threading |
MG_ENABLE_SSI |
0 | Enable serving SSI files by mg_http_serve_dir() |
MG_IO_SIZE |
512 | Granularity of the send/recv IO buffer growth |
MG_MAX_RECV_BUF_SIZE |
(3 * 1024 * 1024) | Maximum recv buffer size |
MG_MAX_HTTP_HEADERS |
40 | Maximum number of HTTP headers |
NOTE: MG_IO_SIZE
controls the maximum UDP message size, see https://github.com/cesanta/mongoose/issues/907 for details. If application uses large UDP messages, increase the MG_IO_SIZE
limit accordingly.
Minimal HTTP server#
This example is a simple static HTTP server that serves current directory:
#include "mongoose.h"
static const char *s_web_root_dir = ".";
static const char *s_listening_address = "http://localhost:8000";
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_http_serve_opts opts = {.root_dir = s_web_root_dir};
if (ev == MG_EV_HTTP_MSG) mg_http_serve_dir(c, ev_data, &opts);
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, s_listening_address, cb, &mgr);
for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return 0;
}
Minimal TCP echo server#
This example is a simple TCP echo server that listens on port 1234:
#include "mongoose.h"
static const char *s_listening_address = "tcp://0.0.0.0:1234";
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_RECV) {
mg_send(c, c->recv.buf, c->recv.len); // Echo received data back
mg_iobuf_delete(&c->recv, c->recv.len); // And discard it
}
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_listen(&mgr, s_listening_address, cb, &mgr);
for (;;) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
return 0;
}
API Reference#
Core#
struct mg_mgr#
struct mg_mgr {
struct mg_connection *conns; // List of active connections
struct mg_connection *dnsc; // DNS resolver connection
const char *dnsserver; // DNS server URL
int dnstimeout; // DNS resolve timeout in milliseconds
};
Event management structure that holds a list of active connections, together with some housekeeping information.
struct mg_connection#
struct mg_connection {
struct mg_connection *next; // Linkage in struct mg_mgr :: connections
struct mg_mgr *mgr; // Our container
struct mg_addr peer; // Remote peer address
void *fd; // Connected socket, or LWIP data
struct mg_iobuf recv; // Incoming data
struct mg_iobuf send; // Outgoing data
mg_event_handler_t fn; // User-specified event handler function
void *fn_data; // User-speficied function parameter
mg_event_handler_t pfn; // Protocol-specific handler function
void *pfn_data; // Protocol-specific function parameter
char label[32]; // Arbitrary label
void *tls; // TLS specific data
unsigned is_listening : 1; // Listening connection
unsigned is_client : 1; // Outbound (client) connection
unsigned is_accepted : 1; // Accepted (server) connection
unsigned is_resolving : 1; // Non-blocking DNS resolv is in progress
unsigned is_connecting : 1; // Non-blocking connect is in progress
unsigned is_tls : 1; // TLS-enabled connection
unsigned is_tls_hs : 1; // TLS handshake is in progress
unsigned is_udp : 1; // UDP connection
unsigned is_websocket : 1; // WebSocket connection
unsigned is_hexdumping : 1; // Hexdump in/out traffic
unsigned is_draining : 1; // Send remaining data, then close and free
unsigned is_closing : 1; // Close and free the connection immediately
unsigned is_readable : 1; // Connection is ready to read
unsigned is_writable : 1; // Connection is ready to write
};
A connection - either a listening connection, or an accepted connection, or an outbout connection.
mg_mgr_init()#
void mg_mgr_init(struct mg_mgr *);
Initialise event manager structure: set a list of active connections to NULL, set DNS server and timeout to their default values, etc.
mg_mgr_poll()#
void mg_mgr_poll(struct mg_mgr *mgr, int ms);
Perform a single poll iteration. For each connection in the mgr->conns
list,
- See if there is incoming data. If it is, read it into the
c->recv
buffer, sendMG_EV_RECV
event - See if there is data in the
c->send
buffer, and write it, sendMG_EV_WRITE
event - If a connection is listening, accept an incoming connection if any, and send
MG_EV_ACCEPT
event to it - Send
MG_EV_POLL
event
Each connection has two event handler functions: c->fn
and c->pfn
. The c->fn
is a user-specified event handler function. The c->pfn
is a protocol-specific handler function that is set implicitly. For example, a mg_http_listen()
sets c->pfn
to a Mongoose's HTTP event handler. A protocol-specific handler is called before user-specific handler. It parses incoming data and may invoke protocol-specific events like MG_EV_HTTP_MSG
.
mg_mgr_free()#
void mg_mgr_free(struct mg_mgr *mgr);
Close all connections, and free all resources.
mg_listen()#
struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
Create a listening connection, append this connection to mgr->conns
.
url
- specifies local IP address and port to listen on, e.g.tcp://127.0.0.1:1234
orudp://0.0.0.0:9000
fn
- an event handler functionfn_data
- an arbitrary pointer, which will be passed asfn_data
when an event handler is called. This pointer is also stored in a connection structure asc->fn_data
Return value: created connection, or NULL
on error.
mg_connect()#
struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
Create an outbout connection, append this connection to mgr->conns
.
url
- specifies remote IP address/port to connect to, e.g.http://a.com
fn
- an event handler functionfn_data
- an arbitrary pointer, which will be passed asfn_data
when an event handler is called. This pointer is also stored in a connection structure asc->fn_data
Return value: created connection, or NULL
on error.
mg_send()#
int mg_send(struct mg_connection *c, const void *data, size_t size);
Append data
of size size
to the c->send
buffer. Return number of bytes appended.
Note: this function does not push data to the network! It only appends data to the output buffer. The data is being sent when mg_mgr_poll()
is called. If mg_send()
is called multiple times, the output buffer grows.