Skip to content

Server WebSockets

The Ioto web server supports WebSockets for bidirectional, full-duplex communications over persistent TCP/IP connections. A WebSocket connection is established over a standard HTTP connection using a GET request and is then upgraded without impacting the original connection.

Read the WebSockets Library for background first.

Configuration

To configure WebSockets in the web server, define a request route for web sockets requests. This route ill use a unique URI prefix for WebSocket communications and configure the action handler to be invoked upon receipt of a connection requeset.

routes: [
    { match: '/upload/', methods: ['DELETE', 'GET', 'PUT'] },
    { match: '/ws/', methods: ['GET'], handler: 'action' },
    { /* Catch all */ },
],

WebSockets is configured via several web.json5 configuration directives.

PathDefaultDescription
limits.maxMessageMAXINTDefine the maximum message size.
limits.maxFrame131072Define the maximum frame size.
webSockets.pingnoneDefine the ping keep-alive message frequency.
webSockets.protocolchatDefine the application-level sub-protocol.
webSockets.validateUTFtrueEnable validation of UTF8 message.

When WebSockets messages are sent, they may be divided into frames. On receipt, the client will aggregate into a unified message.

API Quick Tour

This is a quick tour of the WebSockets server API.

When a WebSockets connection request is received by the Ioto web server, the HTTP upgrade header will instruct Ioto to upgrade the connection to use WebSockets.

To respond to the connection, you should define an action routine to be invoked when the connection is established. To define the action function, specify the request URL and provide the function to invoke. This definition should be placed in your Ioto ioStart routine.

c
webAddAction(host, "/test/websockets", myAction, NULL);

When a WebSockets request is received, your myAction function will be invoked:

c
static void myAction(Web *web)
{
    webAsync(web, onRead, web);
    //  Run web sockets until closed and invoke onEvent for each incoming message and event
    if (webWait(web, onEvent) < 0) {
        //  Error
    }
    // Closed
}

The webAsync routine defines a callback routine to invoke for incoming WebSocket messages, errors and connection events. The webWait routine blocks until the WebSocket connection is closed.

If the connection is closed, webWait will return a value <= 0. If less than zero, the socket was closed due to an error. If the API returns zero, the connection has been orderly closed.

c
void onEvent(WebSocket *ws, int event, char *buf, ssize len, void *arg)
{
    switch (event) {
    case WS_EVENT_OPEN:
    case WS_EVENT_CLOSE:
    case WS_EVENT_ERROR:
        break;

    case WS_EVENT_MESSAGE:
    case WS_EVENT_PARTIAL_MESSAGE:
        printf("Received %s\n", buf);
        webSocketSend(ws, "%s", "Response message");
        break;
    }
}

To send a message to the peer, use webSocketSend:

c
//  Send a message to the client
webSocketSend(web->webSocket, "WebSocket connected");

To close the connection, you can call webSocketSendBlock with a close message type.

c
webSocketSendClose(web->webSocket, status, reason);

or alternatively, call webFinalize which will close the HTTP connection and the WebSocket connection.

c
webFinalize(web);

Keep Alive

To keep a communications channel alive, it is sometimes necessary to send regular messages to indicate the channel is still being used. Some servers, browsers, or proxies may close an idle connection. The Ping/Pong WebSockets messages are designed to send non-application-level traffic that will prevent the channel from being prematurely closed.

Automatic ping message can be sent from the web server by setting the webSockets.ping directive to the desired frequency. For example:

json5
webSockets {
    ping: "1min"
}

Timeouts

The standard Ioto request and inactivity timeouts can be used for WebSocket communications by defining the timeouts.request and timeouts.inactivity web.json5 directives. These are the same request and inactivity timeouts used for ordinary requests.

You can extend the timeouts by calling webUpdateDeadline in your onRead callback whenever a message is received or sent.

timeouts: {
    inactivity: '5 mins',
    request: '1 hour',
},

API

References