Updated Mar 2025
Transport Interface
Introduction
coreMQTT and coreHTTP has no dependency on any particular TCP/IP stack. Therefore, in order to use either the coreMQTT or coreHTTP library, you'll need to provide a Transport Interface structure. An instance of the Transport Interface contains function pointers and context data required to send and receive data on a single network connection. The FreeRTOS distribution includes example implementations of the Transport Interface which you can use in your applications.
Custom Implementations
Applications can provide their own implementation of the Transport Interface if the example implementations don't match the underlying network or TLS stack. An implementation has two parts. The first is the definition of a network context data structure that wraps an underlying data stream, such as a socket handle or a TLS context. The second is a pair of functions that can send and receive data on that network context - these two functions just wrap whichever network send and receive functions you are already using within functions that have the prototype expected by the transport interface structure. There is a worked example below.
The Transport Interface structure is defined as follows:
1/*2 * The NetworkContext is an incomplete type. An implementation of this3 * interface must define struct NetworkContext for the system's requirements.4 * For example, a plain-text implemention of the NetworkContext type might5 * include a socket, and a TLS implementation might add a TLS context. This6 * context is passed into the network interface send() and recv() functions.7 */8struct NetworkContext;9typedef struct NetworkContext NetworkContext_t;1011/*12 * @brief Transport interface for receiving data on the network.13 *14 * @note It is RECOMMENDED that the transport receive implementation15 * does NOT block when requested to read a single byte. A single byte16 * read request can be made by the caller to check whether there is a17 * new frame available on the network for reading.18 * However, the receive implementation MAY block for a timeout period when19 * it is requested to read more than 1 byte. This is because once the caller20 * is aware that a new frame is available to read on the network, then21 * the likelihood of reading more than one byte over the network becomes high.22 *23 * @param[in, out] pNetworkContext Implementation-defined network context.24 * @param[in, out] pBuffer Buffer to receive the data into.25 * @param[in] bytesToRecv Number of bytes requested from the network.26 *27 * @return The number of bytes received or a negative value to indicate28 * error.29 *30 * @note If no data is available on the network to read and no error31 * has occurred, zero MUST be the return value. A zero return value32 * SHOULD represent that the read operation can be retried by calling33 * the API function. Zero MUST NOT be returned if a network disconnection34 * has occurred.35 */36typedef int32_t ( * TransportRecv_t )( NetworkContext_t * pNetworkContext,37 void * pBuffer,38 size_t bytesToRecv );3940/*41 * @brief Transport interface for sending data over the network.42 *43 * @note It is RECOMMENDED that a non-fatal failure in transmitting bytes over44 * the network, like a full transmit buffer of the underlying TCP stack, is45 * treated as a retriable error operation by returning a zero return value.46 *47 * @param[in, out] pNetworkContext Implementation-defined network context.48 * @param[in] pBuffer Buffer containing the bytes to send over the network.49 * @param[in] bytesToSend Number of bytes to send over the network.50 *51 * @return The number of bytes sent or a negative value to indicate error.52 *53 * @note If no data is transmitted over the network and no network error54 * has occurred, this MUST return zero as the return value.55 * A zero return value SHOULD represent that the send operation can be retried56 * by calling the API function. Zero MUST NOT be returned if a network57 * disconnection has occurred.58 */59typedef int32_t ( * TransportSend_t )( NetworkContext_t * pNetworkContext,60 const void * pBuffer,61 size_t bytesToSend );6263typedef struct TransportInterface64{65 TransportRecv_t recv; /* Receive function (see above) */66 TransportSend_t send; /* Send function (see above) */67 NetworkContext_t * pNetworkContext; /* Network context (see above) */68} TransportInterface_t;69
Transport Interface Structure
Worked Example
This example describes how to create a transport interface for the FreeRTOS-Plus-TCP stack (this is for demonstration purposes only as the FreeRTOS source code download already contains a transport interface for FreeRTOS-Plus-TCP). For simplicity the example is using TCP without TLS or other form of encryption. Production IoT devices should always use encrypted connections and the FreeRTOS download includes transport interfaces that use FreeRTOS-Plus-TCP with TLS stacks.
Starting point
It is recommended to ensure your application is able to successfully send and receive from the network before creating a network transport interface - that way the transport interface is just wrapping send and receive functions that are already working.
Defining the NetworkContext structure
FreeRTOS-Plus-TCP sockets are stored in variables of type Socket_t. So the NetworkContext structure can be defined as:
1/* The network context just contains the FreeRTOS-Plus-TCP socket2 * (Note: production systems should use TLS, not just the underlying3 * socket, so could use the TLS context here instead of the socket) */4struct NetworkContext5{6 Socket_t tcpSocket;7};
Defining a NetworkContext structure that just contains a FreeRTOS-Plus-TCP socket.
Implementing the send and receive wrappers
Next, the FreeRTOS-Plus-TCP send and receive functions need to be wrapped by functions that have the prototype expected by the transport interface's send and receive functions. The example below also demonstrates how to obtain the socket used by the send and receive functions from the NetworkContext parameter:
1/* The prototypes of the following send and receive functions match that2 expected by the transport interface's function pointers. These simple3 implementations show how to use the network context structure defined4 above. */56int32_t transport_recv( NetworkContext_t * pNetworkContext,7 void * pBuffer,8 size_t bytesToRecv )9{10 int32_t socketStatus = 1;1112 /* The TCP socket may have a receive block time. If bytesToRecv is greater13 * than 1, then a frame is likely already part way through reception and14 * blocking to wait for the desired number of bytes to be available is the15 * most efficient thing to do. If bytesToRecv is 1, then this may be a16 * speculative call to read to find the start of a new frame, in which case17 * blocking is not desirable as it could block an entire protocol agent18 * task for the duration of the read block time and therefore negatively19 * impact performance. So if bytesToRecv is 1, then don't call recv unless20 * it is known that bytes are already available. */21 if( bytesToRecv == 1 )22 {23 socketStatus = ( int32_t ) FreeRTOS_recvcount( pPlaintextTransportParams->tcpSocket );24 }2526 if( socketStatus > 0 )27 {28 socketStatus = FreeRTOS_recv( pNetworkContext->tcpSocket, pBuffer, bytesToRecv, 0 );29 }3031 return socketStatus;32}3334int32_t transport_send( NetworkContext_t * pNetworkContext,35 const void * pBuffer,36 size_t bytesToSend )37{38 int32_t socketStatus;3940 socketStatus = FreeRTOS_send( pNetworkContext->tcpSocket, pBuffer, bytesToSend, 0 );4142 if( socketStatus == -pdFREERTOS_ERRNO_ENOSPC )43 {44 /* The TCP buffers could not accept any more bytes so zero bytes were sent.45 * This is not necessarily an error that should cause a disconnect unless46 * it persists so return 0 bytes received rather than an error. */47 socketStatus = 0;48 }4950 return socketStatus;51}52
Implementing the transport send and receive functions for FreeRTOS-Plus-TCP
Populating the TransportInterface_t structure
Finally, the code below shows how to populate the transport interface structure with the NetworkContext structure, and transport_send() and transport_recv() functions defined above:
1/* Populating the TransportInterface_t structure with the definitions above. */2void init_transport_from_socket( Socket_t tcpSocket,3 NetworkContext_t * pNetworkContext,4 TransportInterface_t * pTransport )5{6 pNetworkContext->tcpSocket = tcpSocket;7 pTransport->recv = transport_recv;8 pTransport->send = transport_send;9 pTransport->pNetworkContext = pNetworkContext;10}11
Defines the NetworkContext declared in transport_interface.h
Example Implementations
Examples for both plain-text and TLS communication are included.
We strongly recommend that production applications use TLS for communication. This provides a Transport
Interface which is both authenticated and encrypted, as demonstrated in
the MQTT TLS demo.
The transport interface implementations included in the FreeRTOS download are split into two files -
a wrapper C file specific to the TCP stack, and a supplemental C file specific to using a TLS stack with
the selected TCP stack. For example, to use FreeRTOS-Plus-TCP with mbedTLS, build sockets_wrapper.c from
the network_transport/freertos_plus_tcp directory
in the source code distribution, then build using_mbedtls.c from the using_mbedtls subdirectory.