A WebSocket server written in C++17 from scratch, with no external dependencies beyond OpenSSL (used only for SHA-1 during the handshake). It implements the RFC 6455 protocol directly on top of POSIX sockets.
- Accepts WebSocket connections over TCP
- Performs the HTTP Upgrade handshake
- Decodes and encodes WebSocket frames (text, binary, ping/pong, close, continuation)
- Handles fragmented messages
- Manages multiple concurrent connections using
poll() - Responds to ping frames with pong
- Handles graceful close handshake with proper close codes
- Shuts down cleanly on
SIGINT/SIGTERM
The server uses a single-threaded event loop with poll() for I/O multiplexing. Each connection moves through a state machine:
CONNECTING → OPEN → CLOSING → CLOSED
Components:
- Server: Manages the listen socket and connection pool. Runs the event loop and accepts new connections.
- Connection: Handles per-client state, processes handshakes, and manages frame I/O. Enforces protocol rules (masking, fragmentation, close handshake).
- FrameHandler: Encodes and decodes WebSocket frames per RFC 6455. Handles payload length encoding (7-bit, 16-bit, 64-bit) and masking/unmasking.
- Handshake: Parses HTTP Upgrade requests and generates
Sec-WebSocket-Acceptresponses. - TcpSocket: Thin wrapper around POSIX socket APIs.
- Linux (uses POSIX sockets and
poll()) - g++ with C++17 support
makeThis produces the ws_server binary in the project root.
To remove build artifacts:
make clean./ws_serverThe server listens on port 9002 by default (configured in main.cpp).
Browser:
const ws = new WebSocket("ws://localhost:9002");
ws.onopen = () => {
console.log("Connected");
ws.send("hello");
};
ws.onmessage = (e) => console.log("Received:", e.data);
ws.onclose = (e) => console.log("Closed:", e.code, e.reason);Command line (wscat):
# Install wscat
npm install -g wscat
# Connect
wscat -c ws://localhost:9002Implemented:
- HTTP Upgrade handshake with
Sec-WebSocket-Acceptvalidation - Frame encoding/decoding (all opcodes: text, binary, close, ping, pong, continuation)
- Client-to-server masking enforcement
- Message fragmentation and reassembly
- Close handshake with status codes (1000-1011)
- Ping/pong keep-alive mechanism
- 16 MB max payload size per frame
Not implemented:
- WebSocket extensions (per-message compression, multiplexing)
- TLS/SSL (
wss://connections) - Subprotocol negotiation (
Sec-WebSocket-Protocol) - Origin validation
- UTF-8 validation for text frames
- Single-threaded: All connections share one event loop. No parallel processing.
- Echo/log only: Received messages are printed to stdout, not broadcast or persisted.
- No TLS: Runs plain
ws://only. Nowss://support. - No connection broadcasting: Messages aren't forwarded between clients.
- No authentication: All connections are accepted without validation.
- Fixed buffer size: 4 KB recv buffer may require multiple reads for large frames.
This project is licensed under the MIT License. See LICENSE for details.