6.0.0-rc4Source

Using Nt WebSockets

Connecting to a WebSocket server, accepting connections, messages, closing, TLS, and WebSockets on the port of an HTTP server.

Link Nt::WebSockets (or Nt6::WebSockets from an installed package) and include <QtWebSockets>:

find_package(Nt6 6 REQUIRED)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE Nt6::WebSockets)

A Server

A QWebSocketServer listens on a port and emits newConnection() for each client that has completed the opening handshake. Take the socket with nextPendingConnection() and connect to its signals:

QWebSocketServer server(QStringLiteral("Nt echo"), QWebSocketServer::NonSecureMode);
if (!server.listen(QHostAddress::LocalHost, port)) {
    std::fprintf(stderr, "cannot listen: %s\n", qPrintable(server.errorString()));
    return 1;
}
QObject::connect(&server, &QWebSocketServer::newConnection, [&server] {
    QWebSocket *socket = server.nextPendingConnection();
    QObject::connect(socket, &QWebSocket::textMessageReceived, socket,
                     [socket](const QString &message) { socket->sendTextMessage(message); });
    QObject::connect(socket, &QWebSocket::binaryMessageReceived, socket,
                     [socket](const QByteArray &message) { socket->sendBinaryMessage(message); });
    QObject::connect(socket, &QWebSocket::disconnected, socket, &QObject::deleteLater);
});

The sockets are children of the server. Delete each one when it disconnects, as above, so that a long-running server does not collect them.

A Client

QWebSocket::open() connects to a ws: or wss: URL. Messages can be sent once connected() has been emitted:

QWebSocket socket;
QObject::connect(&socket, &QWebSocket::connected, [&] {
    std::printf("connected, subprotocol \"%s\"\n", qPrintable(socket.subprotocol()));
    socket.sendTextMessage(text);
});
QObject::connect(&socket, &QWebSocket::textMessageReceived, [&](const QString &message) {
    std::printf("received: %s\n", qPrintable(message));
    socket.close();
});
QObject::connect(&socket, &QWebSocket::disconnected, &app, &QCoreApplication::quit);
QObject::connect(&socket, &QWebSocket::errorOccurred, [&](QAbstractSocket::SocketError) {
    std::fprintf(stderr, "error: %s\n", qPrintable(socket.errorString()));
    app.exit(1);
});

errorOccurred() reports a connection that cannot be made, and an opening handshake that the server refuses; errorString() says why.

Messages

A message is text or binary. sendTextMessage() sends a QString as UTF-8, and sendBinaryMessage() sends bytes. The receiving side emits textMessageReceived() or binaryMessageReceived() with the whole message. Long messages travel in several frames (see QWebSocket::setOutgoingFrameSize()); textFrameReceived() and binaryFrameReceived() report them one by one, for programs that want to process a message before it has all arrived.

Text must be valid UTF-8. A peer that sends anything else is disconnected with QWebSocketProtocol::CloseCodeWrongDatatype. Messages larger than QWebSocket::maxAllowedIncomingMessageSize(), 64 MiB by default, are refused with QWebSocketProtocol::CloseCodeTooMuchData.

Subprotocols

A client can ask for an application protocol by name, and the server picks the first one it supports:

server.setSupportedSubprotocols({QStringLiteral("echo.v2"), QStringLiteral("echo.v1")});

Closing

close() sends a close frame with a code and a reason; the peer answers with its own, and the connection closes. Both sides then emit disconnected(), and closeCode() and closeReason() tell what was sent. QWebSocketProtocol::CloseCodeAbnormalDisconnection means the connection broke without a closing handshake.

Pings

Both sides answer pings automatically. ping() sends one, and pong() reports the round-trip time. A program can ping at intervals to notice a connection that has silently gone away.

TLS

A client connects to a wss: URL with TLS, using the system's trusted certificates. A server in QWebSocketServer::SecureMode needs a certificate and a private key:

QWebSocketServer server("Secure echo", QWebSocketServer::SecureMode);
QSslConfiguration tls = QSslConfiguration::defaultConfiguration();
tls.setLocalCertificate(certificate);
tls.setPrivateKey(key);
server.setSslConfiguration(tls);
server.listen(QHostAddress::Any, 443);

WebSockets on an HTTP Server

QAbstractHttpServer accepts WebSocket upgrades on the port it serves. Add a verifier that accepts the requests you want, and take the sockets when newWebSocketConnection() is emitted:

// WebSocket upgrades of /ws are accepted on the same port; the sockets echo what they receive.
server.addWebSocketUpgradeVerifier(&server, [](const QHttpServerRequest &request) {
    if (request.url().path() == "/ws")
        return QHttpServerWebSocketUpgradeResponse::accept();
    return QHttpServerWebSocketUpgradeResponse::passToNext();
});
QObject::connect(&server, &QHttpServer::newWebSocketConnection, [&server] {
    QWebSocket *socket = server.nextPendingWebSocketConnection().release();
    QObject::connect(socket, &QWebSocket::textMessageReceived, socket,
                     [socket](const QString &message) { socket->sendTextMessage(message); });
    QObject::connect(socket, &QWebSocket::disconnected, socket, &QObject::deleteLater);
});

Origins

Browsers let any page open a WebSocket to any server, and send the page's origin in the Origin header. A server whose clients are its own pages should check it, through QWebSocketServer::originAuthenticationRequired() or, on an HTTP server, the request's headers in the upgrade verifier.