WebSocket Usage Guide
Overview
UVHTTP provides full WebSocket support, allowing you to easily implement real-time bidirectional communication. The WebSocket protocol is built on top of the HTTP protocol, upgrading from HTTP to WebSocket through a handshake phase.
How WebSocket Works
The Handshake Process
The client makes an HTTP request containing special headers:
Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: <random string> Sec-WebSocket-Version: 13The server responds to the upgrade request:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: <computed string>Once the connection is established, both parties can send messages in both directions
Basic Usage
Creating a WebSocket Server
#include "uvhttp.h"
// WebSocket connection established callback
int on_connect(uvhttp_ws_connection_t* ws_conn, void* user_data) {
(void)user_data;
printf("WebSocket connection established\n");
return 0;
}
// WebSocket message received callback
int on_message(uvhttp_ws_connection_t* ws_conn,
const char* data,
size_t len,
int opcode,
void* user_data) {
(void)ws_conn;
(void)user_data;
printf("Received message: %.*s\n", (int)len, data);
// Echo the message
uvhttp_server_ws_send(ws_conn, data, len);
return 0;
}
// WebSocket connection closed callback
int on_close(uvhttp_ws_connection_t* ws_conn, void* user_data) {
(void)ws_conn;
(void)user_data;
printf("WebSocket connection closed\n");
return 0;
}
// WebSocket error callback
int on_error(uvhttp_ws_connection_t* ws_conn,
int error_code,
const char* error_msg,
void* user_data) {
(void)ws_conn;
(void)user_data;
printf("WebSocket error: %d - %s\n", error_code, error_msg);
return 0;
}
int main() {
uv_loop_t* loop = uv_default_loop();
uvhttp_server_t* server = NULL;
uvhttp_server_new(loop, &server);
uvhttp_router_t* router = NULL;
uvhttp_router_new(&router);
uvhttp_server_set_router(server, router);
// Register the WebSocket handler
uvhttp_ws_handler_t ws_handler = {
.on_connect = on_connect,
.on_message = on_message,
.on_close = on_close,
.on_error = on_error,
.user_data = NULL
};
uvhttp_server_register_ws_handler(server, "/ws", &ws_handler);
// Start the server
uvhttp_server_listen(server, "0.0.0.0", 8080);
printf("WebSocket server running at http://localhost:8080/ws\n");
uv_run(loop, UV_RUN_DEFAULT);
// Clean up resources
uvhttp_server_free(server);
return 0;
}WebSocket Message Types
WebSocket supports several message types (opcodes):
0x0: Continuation Frame0x1: Text Frame0x2: Binary Frame0x8: Close Frame0x9: Ping Frame (heartbeat frame)0xA: Pong Frame (heartbeat response frame)
Sending Different Types of Messages
// Send a text message
const char* text = "Hello WebSocket";
uvhttp_server_ws_send(ws_conn, text, strlen(text));
// Send a binary message
const char* binary_data = "\x01\x02\x03\x04";
uvhttp_server_ws_send(ws_conn, binary_data, 4);
// Send a Ping
uvhttp_ws_send_ping(NULL, ws_conn, (const uint8_t*)"ping", 4);
// Send a Close
uvhttp_server_ws_close(ws_conn, 1000, "Normal closure");Application-Level Authentication
Since authentication should be implemented at the application layer, you can accept or reject connections in the on_connect callback. Note that on_connect receives only the connection object — the original HTTP request headers (e.g. Authorization) are not accessible there. Header-based authentication must be performed at the HTTP layer before the WebSocket upgrade completes:
int on_connect(uvhttp_ws_connection_t* ws_conn) {
// on_connect receives only the WebSocket connection; the original HTTP
// request headers (e.g. Authorization) are not available here. Validate
// request headers in your HTTP handlers/middleware before the upgrade,
// then accept or reject the connection here.
printf("WebSocket connection established\n");
return 0; // Accept the connection
}Best Practices
1. Connection Management
// Maintain a list of active connections
static uvhttp_ws_connection_t* g_connections[MAX_CONNECTIONS];
static int g_connection_count = 0;
int on_connect(uvhttp_ws_connection_t* ws_conn, void* user_data) {
(void)user_data;
if (g_connection_count < MAX_CONNECTIONS) {
g_connections[g_connection_count++] = ws_conn;
printf("Connection %d established\n", g_connection_count);
} else {
printf("Connection limit reached\n");
return -1;
}
return 0;
}
int on_close(uvhttp_ws_connection_t* ws_conn, void* user_data) {
(void)user_data;
// Remove from the connection list
for (int i = 0; i < g_connection_count; i++) {
if (g_connections[i] == ws_conn) {
// Move the last element to the current position
g_connections[i] = g_connections[--g_connection_count];
break;
}
}
printf("Connection closed, %d connections remaining\n", g_connection_count);
return 0;
}
// Broadcast a message to all connections
void broadcast_message(const char* message, size_t len) {
for (int i = 0; i < g_connection_count; i++) {
uvhttp_server_ws_send(g_connections[i], message, len);
}
}2. Heartbeat Detection
// Send a Ping periodically
void heartbeat_timer_callback(uv_timer_t* handle) {
const char* ping_msg = "ping";
for (int i = 0; i < g_connection_count; i++) {
uvhttp_ws_send_ping(NULL, g_connections[i], (const uint8_t*)ping_msg,
strlen(ping_msg));
}
// Reset the timer
uv_timer_start(handle, heartbeat_timer_callback, 30000);
}
int main() {
// ... server initialization code ...
// Create the heartbeat timer
uv_timer_t heartbeat_timer;
uv_timer_init(loop, &heartbeat_timer);
uv_timer_start(&heartbeat_timer, heartbeat_timer_callback, 30000); // 30 seconds
// ... start the server ...
}3. Message Size Limits
#define MAX_MESSAGE_SIZE (1024 * 1024) // 1MB
int on_message(uvhttp_ws_connection_t* ws_conn,
const char* data,
size_t len,
int opcode,
void* user_data) {
(void)ws_conn;
(void)opcode;
(void)user_data;
if (len > MAX_MESSAGE_SIZE) {
printf("Message too large: %zu bytes\n", len);
uvhttp_server_ws_close(ws_conn, 1009, "Message too large");
return -1;
}
// Process the message
process_message(data, len);
return 0;
}4. Error Handling
int on_error(uvhttp_ws_connection_t* ws_conn,
int error_code,
const char* error_msg,
void* user_data) {
(void)ws_conn;
(void)user_data;
printf("WebSocket error: %d - %s\n", error_code, error_msg);
// Handle by error type
switch (error_code) {
case 1000: // Normal closure
printf("Client closed normally\n");
break;
case 1002: // Protocol error
printf("Protocol error, closing connection\n");
break;
case 1003: // Unsupported data type
printf("Unsupported data type\n");
break;
default:
printf("Unknown error\n");
}
return 0;
}Client Examples
JavaScript Client
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = function() {
console.log('WebSocket connection established');
ws.send('Hello Server');
};
ws.onmessage = function(event) {
console.log('Received message:', event.data);
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
ws.onclose = function(event) {
console.log('WebSocket connection closed:', event.code, event.reason);
};Python Client
import asyncio
import websockets
async def websocket_client():
uri = "ws://localhost:8080/ws"
async with websockets.connect(uri) as websocket:
print("WebSocket connection established")
# Send a message
await websocket.send("Hello Server")
# Receive messages
async for message in websocket:
print(f"Received message: {message}")
asyncio.run(websocket_client())curl Testing
# Test the WebSocket connection with websocat
websocat ws://localhost:8080/ws
# Send a message
Hello Server
# Receive the echoed message
Hello ServerFAQ
Q: How do I limit the number of connections?
A: Maintain a connection list in the on_connect callback and return -1 to reject the connection when the limit is reached.
Q: How do I implement room/channel functionality?
A: Have the client send a join-room message when connecting, and maintain a mapping from rooms to connections on the server.
Q: How do I handle large messages?
A: Check the message size in the on_message callback and close the connection when it exceeds the limit.
Q: How do I implement message compression?
A: Compress the data with a compression algorithm (such as zlib) before sending; the client decompresses it upon receipt.
Q: How do I handle disconnection and reconnection?
A: Implement automatic reconnection logic on the client, and maintain connection state on the server.