Use this when the job is not "serve HTTP" but "upgrade and speak frames."
icey's WebSocket support lives in the HTTP module for a reason. The handshake is HTTP. After that, the connection is upgraded and the frame adapter takes over. That is the whole model.
Client side:
createConnectionT(ws:// or wss://)
-> start()
-> HTTP handshake
-> Connect fires when the WebSocket handshake is complete
-> send/receive framed payloadServer side:
HTTP request with Upgrade: websocket
-> replaceAdapter(ws::ConnectionAdapter)
-> send 101 Switching Protocols
-> connection enters Upgraded mode
-> frame I/O from there onIf you need the underlying connection rules, read HTTP Lifecycle. This page is about the usable WebSocket path.
The current sample does exactly this:
auto conn = http::createConnectionT<http::ClientConnection>(
http::URL("ws://echo.websocket.events"));
conn->Connect += [&]() {
conn->send("Hello!", 6, http::ws::Text);
};
conn->Payload += [](const MutableBuffer& buf) {
std::string data(bufferCast<const char*>(buf), buf.size());
std::cout << "Received: " << data << '\n';
};
conn->Close += [](http::Connection&) {
uv_stop(uv::defaultLoop());
};
conn->start();Two rules matter here:
start() starts the handshakeConnect fires after the WebSocket handshake succeeds, not after bare TCP connectThat second rule is important. It means application code does not race the handshake.
Outgoing frame type is controlled by the flags argument to send():
conn->send(text.data(), text.size(), http::ws::Text);
conn->send(binary.data(), binary.size(), http::ws::Binary);You are sending payload. The adapter handles framing.
On the server side, upgrade is explicit:
srv.Connection += [](http::ServerConnection::Ptr conn) {
if (conn->request().get("Upgrade") == "websocket") {
conn->replaceAdapter(
std::make_unique<http::ws::ConnectionAdapter>(
conn.get(), http::ws::ServerSide));
return;
}
conn->response().setStatus(http::StatusCode::OK);
conn->sendHeader();
conn->close();
};After that point:
send() goes through the WebSocket adapterPayload is application payload, not raw frame bytesThat last point is one of the main benefits of the adapter layer.
The application does not have to reassemble WebSocket fragmentation by hand.
icey's framer:
Payload receives complete application messages, not raw frame fragments.
That is how it should be. Fragmentation is transport detail, not application protocol.
Ping and pong are handled inside the adapter.
Ping gets a PongPong is consumed internallyIf you want to close explicitly, call shutdown() on the adapter:
auto* adapter = static_cast<http::ws::ConnectionAdapter*>(conn->adapter());
adapter->shutdown(
static_cast<uint16_t>(http::ws::CloseStatusCode::NormalClose),
"goodbye");That is different from just tearing down the underlying TCP socket. It gives the peer a proper WebSocket close frame first.
icey handles the RFC rules that should not be application boilerplate:
Application code still owns:
If a WebSocket path is not working, debug it in this order:
Do not start by debugging your JSON message layer if the upgrade never completed.
wsclient for the runnable client sample