TCP client and server

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, and either tcps:// or tls:// is used in the URLs

Initialization and main loop

In the main() function we first initialize an event manager:

Then we create a timer to handle reconnections; start our server, and start the event loop.

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.

Starting the server at the main function

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

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. We indicate Mongoose that we have consumed that data (so it can clear its buffers) by setting the received data length to zero: r->len = 0
  • 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. We indicate Mongoose that we have consumed that data (so it can clear its buffers) by setting the received data length to zero: r->len = 0
  • 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

  • Follow the Build Tools tutorial to setup your development environment.

  • Start a terminal in the project directory; 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

Check the "How to build" section of the TLS tutorial for specific information on building options for your OS

For more information on developing TLS clients and servers, check the TLS tutorial