Overview

This simple tutorial demonstrates how Mongoose Library can be used to implement TCP clients and servers, even over TLS.

  • We create a client and a server.
  • The client connects to the server and after a brief interval sends some text.
  • The server echoes that text back and the client disconnects.

This dialog will be encrypted if the code is compiled with support for TLS.

Starting the server at the main function

In the main() function we initialize an event manager, create a timer to handle reconnections, start our server, and start the event loop.

  • The server connection is created by listening on a port; by calling mg_listen() and passing it a pointer to the URL, and a pointer to the event handler function, with an optional argument.

We pass the timer creation function a pointer to the event manager, as we will need it later to create the client connection.

For information on using timers, check the timers tutorial.

Server event handler

The server event handler function is called by the event manager every time there is an event to be handled, here we will use:

  • MG_EV_ERROR: There was an error, an error reason is passed as a char * pointer in parameter ev_data; check the error handling tutorial for more information.
  • MG_EV_OPEN: The connection has been created, or initialized after a closure. Here we will also check for the is_listening flag to detect the creation call and log it
  • MG_EV_ACCEPT: The server accepted a connection from a client. If the code has been compiled with TLS support, we initialize TLS by calling mg_tls_init()
  • MG_EV_READ: There is outstanding data, received from the socket. We will log it and echo it back by calling mg_send() with a pointer to the data and the number of bytes to be sent.
  • MG_EV_CLOSE: The connection has closed

Starting the client at a timer event handler

The timer event handler implements the reconnection logic. It creates a client connection c_res.c if it is NULL. This pattern should be used for any type of client connection that must be kept alive and is explained in the error handling tutorial.

  • The client connection is created by calling mg_connect() and passing it a pointer to the URL, and a pointer to the event handler function, with an optional argument. In our case, we'll be passing it a structure providing a place where to hold the connection handler and an integer for internal use.
  • When the client will finish, it will close the connection and set c_res.c to NULL, so the next timer event handler run will detect this and restart the connection.

Client event handler

The client event handler function is called by the event manager every time there is an event to be handled, here we will use:

  • MG_EV_ERROR: There was an error, an error reason is passed as a char * pointer in parameter ev_data; check the error handling tutorial for more information.
  • MG_EV_OPEN: The connection has been created
  • MG_EV_CONNECT: The client has connected to a server. If the code has been compiled with TLS support, we initialize TLS by calling mg_tls_init(). We also set our variable i to a value different than 0 so we know we have to do something later.
  • MG_EV_READ: There is outstanding data, received from the socket. We will just log it
  • MG_EV_CLOSE: The connection has closed
  • MG_EV_POLL: The event manager is polling us at its timeout interval. We'll take advantage of this call to do something. For example, we'll count polls to roughly wait for 5 seconds and send some data, then wait another 5 seconds and close the connection. We signal the event manager we want to drain and close the connection by setting the is_draining flag.

Build and run

  • If you've not already done so, clone the Mongoose Library repo
    $ git clone https://github.com/cesanta/mongoose
    
  • Build and run the example:
    $ cd mongoose/examples/tcp
    $ make clean all
    
  • Observe the log
    74787e5 2 main.c:53:sfn                 SERVER is listening
    7478849 2 main.c:19:cfn                 CLIENT has been initialized
    747884a 2 main.c:86:timer_fn            CLIENT is connecting
    747884a 2 main.c:21:cfn                 CLIENT connected
    747885d 2 main.c:55:sfn                 SERVER accepted a connection
    7479ab1 2 main.c:40:cfn                 CLIENT sent data
    7479ab1 2 main.c:66:sfn                 SERVER got data: Hi, there
    7479ab1 2 main.c:29:cfn                 CLIENT got data: Hi, there
    747acb2 2 main.c:31:cfn                 CLIENT disconnected
    747acb3 2 main.c:69:sfn                 SERVER disconnected
    

Build with TLS support

The makefile will take care of this, just pass it the proper argument.

  • For openSSL:

    $ make clean all SSL=OPENSSL
    
  • For mbedTLS:

    $ make clean all SSL=MBEDTLS
    
  • If you need to provide extra information, like for example includes and library paths, do it with the EXTRA_CFLAGS variable, like:

    $ make clean all SSL=MBEDTLS EXTRA_CFLAGS="-I/path/to/mbedtls/include -L/path/to/mbedtls/lib"
    

For more information on building TLS clients, check the TLS tutorial