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.