Mistake on this page?
Report an issue in GitHub or email us

Network socket overview

The application programming interface for IP networking is the Socket API. As described in the IP networking section, the Socket API relates to OSI layer 4, the Transport layer. In Mbed OS, the Socket API supports both TCP and UDP protocols.

Sockets

In Mbed OS, this socket API is C++ based but closely follows the functionality from the POSIX standard (IEEE Std 1003.1) and relevant RFC standards. The Socket interface is abstract and protocol agnostic and requires you to specify the protocol only when creating the socket. With libraries and interfaces, you may use the abstract base class, which allows you to port applications from one protocol to another.

General use

The following steps describe the typical application flow:

  1. Initialize a network interface.
  2. Create a socket.
  3. Connect (optional step for datagram protocols).
  4. Send data.
  5. Receive data.
  6. Close the socket.

The following code demonstrates those steps by sending an HTTP query to a server:

// Initialize network interface
EthernetInterface eth;
eth.connect();

// Create a socket
TCPSocket sock;
sock.open(&eth);

// Connect
sock.connect("arm.com", 80);

// Send data
sock.send("GET / HTTP/1.0\r\n\r\n", 18);

// Receive data
char buf[100];
sock.recv(buf, 100);

// Close the socket
sock.close();

Changes in Mbed OS 5.10

The 5.10 release refactors the Mbed OS Socket API. For most of the applications, these changes are not noticeable because the TCPSocket and UDPSocket classes still emulate legacy behavior.

The new design contains an abstract socket interface that applications can use directly. Casting Socket pointers back to TCPSocket or UDPSocket is no longer necessary.

Upcasting any protocol specific class to Socket has no side effect and is a recommended API design. Knowing the exact type is only required when you create the socket.

The new design also emphasizes use of SocketAddress for holding the IP addresses, instead on textual format. SocketAddress is a container class that protocols other than IP can use in the future. Legacy string versions of connect(), bind() and sendto() functions do not exist in the Socket base class, but they exist in TCPSocket and UDPSocket classes.

The new design also renders the TCPServer API unnecessary, moving its functionality directly into TCPSocket itself. The legacy TCPServer class still exists and is fully functional.

Using DNS names

IP stacks operate only on binary IP addresses, but in Internet, servers are known by their symbolic domain name (DNS). To use these names with Socket interface, each name has to be resolved before.

Previously Socket interface contained methods that can directly accept DNS names and port numbers and do the resolving internally. These APIs are not recommended as they might hide problems in DNS system.

Recommended way is to use DNS resolver to convert symbolic names to IP addresses. The following example shows how to use DNS in Mbed OS:

NetworkInteface *net;    // Initialized elsewhere

SocketAddress addr;
nsapi_error_t ret = net->gethostbyname("www.arm.com", &addr);  // Resolve www.arm.com and store into addr

if (ret == NSAPI_ERROR_OK) {
    // Resolving was succesfull
}

See DNS resolver for more information.

Server sockets

Some connection oriented protocols, for example TCP, can also be used for listening incomming connections. To do this

  1. Bind socket to specific port by calling Socket::bind()
  2. Set socket to listening mode by calling Socket::listen()
  3. Accept incomming connection by calling Socket::accept()

Accepting new connection return you a pointer to a new Socket object that can be used to communicate with the connected peer. When done, you should call this socket's close() function to shut down the connection and clean up the reserved resources.

Accepting a connection will leave the original socket to listening mode. You can continue to accept new connections until you destroy the listening socket, or call its close() method.

For connectionless protocols, like UDP, each socket can receive from any peer. Therefore listen() and accept() are not required.

Network socket interfaces and classes

The network socket API provides a common interface for using sockets on network devices. It's a class-based interface, which is familiar to users experienced with other socket APIs.

  • Socket: Abstract base class for all protocols. Specifies the API.
  • UDPSocket: This class provides the ability to send packets of data over UDP.
  • TCPSocket: This class provides the ability to send a stream of data over TCP.
  • SocketAddress: You can use this class to represent the IP address and port pair of a unique network endpoint.
  • Network status: API for monitoring network status changes.
  • DNS resolver: API for resolving DNS names

Network errors

The convention of the network socket API is for functions to return negative error codes to indicate failure. On success, a function may return zero or a non-negative integer to indicate the size of a transaction. On failure, a function must return a negative integer, which is one of the error codes in the nsapi_error_t enum:

/** Enum of standardized error codes
 *
 *  Valid error codes have negative values and may
 *  be returned by any network operation.
 *
 *  @enum nsapi_error
 */
enum nsapi_error {
    NSAPI_ERROR_OK                  =  0,        /*!< no error */
    NSAPI_ERROR_WOULD_BLOCK         = -3001,     /*!< no data is not available but call is non-blocking */
    NSAPI_ERROR_UNSUPPORTED         = -3002,     /*!< unsupported functionality */
    NSAPI_ERROR_PARAMETER           = -3003,     /*!< invalid configuration */
    NSAPI_ERROR_NO_CONNECTION       = -3004,     /*!< not connected to a network */
    NSAPI_ERROR_NO_SOCKET           = -3005,     /*!< socket not available for use */
    NSAPI_ERROR_NO_ADDRESS          = -3006,     /*!< IP address is not known */
    NSAPI_ERROR_NO_MEMORY           = -3007,     /*!< memory resource not available */
    NSAPI_ERROR_NO_SSID             = -3008,     /*!< ssid not found */
    NSAPI_ERROR_DNS_FAILURE         = -3009,     /*!< DNS failed to complete successfully */
    NSAPI_ERROR_DHCP_FAILURE        = -3010,     /*!< DHCP failed to complete successfully */
    NSAPI_ERROR_AUTH_FAILURE        = -3011,     /*!< connection to access point failed */
    NSAPI_ERROR_DEVICE_ERROR        = -3012,     /*!< failure interfacing with the network processor */
    NSAPI_ERROR_IN_PROGRESS         = -3013,     /*!< operation (eg connect) in progress */
    NSAPI_ERROR_ALREADY             = -3014,     /*!< operation (eg connect) already in progress */
    NSAPI_ERROR_IS_CONNECTED        = -3015,     /*!< socket is already connected */
    NSAPI_ERROR_CONNECTION_LOST     = -3016,     /*!< connection lost */
    NSAPI_ERROR_CONNECTION_TIMEOUT  = -3017,     /*!< connection timed out */
};

Nonblocking operation

The network socket API also supports nonblocking operations. The set_blocking() member function changes the state of a socket. When a socket is in nonblocking mode, socket operations return NSAPI_ERROR_WOULD_BLOCK when a transaction cannot immediately complete.

To allow efficient use of nonblocking operations, the socket classes provide a sigio() member function to register a callback on socket state changes. When the socket can successfully receive, send or accept or when an error occurs, the system triggers a callback. It may call the callback spuriously without reason.

You may call the callback in interrupt context, but do not make any read or write calls until it is on a thread.

The following example shows how to set up an asynchronous handler for socket:

nsapi_size_or_error_t send_query(TCPSocket *socket) {
    return socket->send(QUERY, QUERY_LEN);
}

nsapi_size_or_error_t receive_data(TCPSocket *socket) {
    // Simplified example, does not properly handle streaming and appending to buffer
    return socket->recv(my_buffer, remaining_len);
}

void handle_socket_sigio(EventFlags *evt, TCPSocket *socket)
{
    static enum {
        CONNECTING,
        SEND,
        RECEIVE,
        CLOSE,
    } next_state = CONNECTING;

    switch (next_state) {
        case CONNECTING:
            switch(socket->connect("api.ipify.org", 80)) {
                case NSAPI_ERROR_IN_PROGRESS:
                    // Connecting to server
                    break;
                case NSAPI_ERROR_ALREADY:
                    // Now connected to server
                    next_state = SEND;
                    break;
                default:
                    // Error in connection phase
                    next_state = CLOSE;
            }
        case SEND:
            if (send_query(socket) > 0)
                next_state = RECEIVE;
            else
                next_state = CLOSE; // Error
            break;
        case RECEIVE:
            if (receive_data(socket) == NSAPI_ERROR_WOULD_BLOCK)
                break;
            next_state = CLOSE;
            break;
        case CLOSE:
            socket->close();
            evt->set(1); // Signal the main thread
            break;
    }
}

int main() {
    EthernetInterface net;
    net.connect();

    TCPSocket socket;
    socket.open(&net);

    EventFlags completed;
    EventQueue *queue = mbed_event_queue();

    Event<void()> handler = queue->event(handle_socket_sigio, &completed, &socket);

    socket.set_blocking(false);
    socket.sigio(handler);
    handler();                   // Kick the state machine to start connecting

    completed.wait_any(1);
}
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.