Mercurial
comparison seobeo/docs/web_socket_server.md @ 121:7b1719fa918c
[Seobeo] Added web socket server.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 08 Jan 2026 06:45:10 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 120:cbbf78b17cfa | 121:7b1719fa918c |
|---|---|
| 1 # Seobeo WebSocket Server - Usage Guide | |
| 2 | |
| 3 A clean, easy-to-use WebSocket server library that integrates seamlessly with the existing Seobeo HTTP server. | |
| 4 | |
| 5 ## Features | |
| 6 | |
| 7 1. **Automatic Upgrade Handling**: Automatically detects and upgrades HTTP connections to WebSocket | |
| 8 2. **Route-Based Handlers**: Register WebSocket handlers for specific paths (like HTTP routing) | |
| 9 3. **Text and Binary Messages**: Send/receive both text and binary data | |
| 10 4. **Broadcast Support**: Send messages to all connected clients | |
| 11 5. **Connection Management**: Automatic connection tracking and cleanup | |
| 12 6. **Integrated with HTTP Server**: Works alongside existing HTTP routes and static file serving | |
| 13 7. **RFC 6455 Compliant**: Full WebSocket protocol support | |
| 14 | |
| 15 ## API Overview | |
| 16 | |
| 17 ### Initialization and Registration | |
| 18 | |
| 19 ```c | |
| 20 // Initialize WebSocket server system | |
| 21 void Seobeo_WebSocket_Server_Init(); | |
| 22 | |
| 23 // Register a WebSocket handler for a specific path | |
| 24 void Seobeo_WebSocket_Server_Register(const char *path, | |
| 25 Seobeo_WebSocket_Server_Handler handler, void *p_user_data); | |
| 26 ``` | |
| 27 | |
| 28 ### Handler Function Type | |
| 29 | |
| 30 ```c | |
| 31 typedef void (*Seobeo_WebSocket_Server_Handler)( | |
| 32 Seobeo_WebSocket_Server_Connection *p_conn, | |
| 33 Seobeo_WebSocket_Message *p_msg, | |
| 34 void *p_user_data | |
| 35 ); | |
| 36 ``` | |
| 37 | |
| 38 ### Sending Messages | |
| 39 | |
| 40 ```c | |
| 41 // Send to specific client | |
| 42 int32 Seobeo_WebSocket_Server_Send_Text(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 43 const char *text); | |
| 44 int32 Seobeo_WebSocket_Server_Send_Binary(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 45 const uint8 *data, size_t length); | |
| 46 | |
| 47 // Broadcast to all clients | |
| 48 void Seobeo_WebSocket_Server_Broadcast_Text(const char *text); | |
| 49 void Seobeo_WebSocket_Server_Broadcast_Binary(const uint8 *data, size_t length); | |
| 50 ``` | |
| 51 | |
| 52 ### Connection Management | |
| 53 | |
| 54 ```c | |
| 55 // Close specific connection | |
| 56 void Seobeo_WebSocket_Server_Connection_Close(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 57 uint16 code, const char *reason); | |
| 58 ``` | |
| 59 | |
| 60 ## Examples | |
| 61 | |
| 62 ### 1. Simple Echo Server | |
| 63 | |
| 64 ```c | |
| 65 #include "seobeo/seobeo.h" | |
| 66 | |
| 67 void Echo_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 68 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 69 { | |
| 70 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 71 { | |
| 72 printf("Received: %.*s\n", (int)p_msg->length, (char*)p_msg->data); | |
| 73 Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 int main() | |
| 78 { | |
| 79 // Initialize WebSocket routing | |
| 80 Seobeo_WebSocket_Server_Init(); | |
| 81 | |
| 82 // Register echo handler | |
| 83 Seobeo_WebSocket_Server_Register("/echo", Echo_Handler, NULL); | |
| 84 | |
| 85 // Start HTTP server (automatically handles WebSocket upgrades) | |
| 86 Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0); | |
| 87 | |
| 88 return 0; | |
| 89 } | |
| 90 ``` | |
| 91 | |
| 92 ### 2. Chat Room Server | |
| 93 | |
| 94 ```c | |
| 95 void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 96 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 97 { | |
| 98 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 99 { | |
| 100 char message[2048]; | |
| 101 snprintf(message, sizeof(message), "[%s]: %.*s", | |
| 102 p_conn->client_id, (int)p_msg->length, (char*)p_msg->data); | |
| 103 | |
| 104 printf("Broadcasting: %s\n", message); | |
| 105 Seobeo_WebSocket_Server_Broadcast_Text(message); | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 int main() | |
| 110 { | |
| 111 Seobeo_WebSocket_Server_Init(); | |
| 112 Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL); | |
| 113 | |
| 114 printf("Chat server started on ws://localhost:8080/chat\n"); | |
| 115 Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0); | |
| 116 | |
| 117 return 0; | |
| 118 } | |
| 119 ``` | |
| 120 | |
| 121 ### 3. Binary Data Broadcasting | |
| 122 | |
| 123 ```c | |
| 124 void Binary_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 125 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 126 { | |
| 127 if (p_msg->opcode == SEOBEO_WS_OPCODE_BINARY) | |
| 128 { | |
| 129 printf("Received %zu bytes, broadcasting...\n", p_msg->length); | |
| 130 Seobeo_WebSocket_Server_Broadcast_Binary(p_msg->data, p_msg->length); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 int main() | |
| 135 { | |
| 136 Seobeo_WebSocket_Server_Init(); | |
| 137 Seobeo_WebSocket_Server_Register("/binary", Binary_Handler, NULL); | |
| 138 | |
| 139 Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0); | |
| 140 return 0; | |
| 141 } | |
| 142 ``` | |
| 143 | |
| 144 ### 4. Multiple Endpoints | |
| 145 | |
| 146 ```c | |
| 147 void Echo_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 148 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 149 { | |
| 150 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 151 Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data); | |
| 152 } | |
| 153 | |
| 154 void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 155 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 156 { | |
| 157 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 158 { | |
| 159 char msg[2048]; | |
| 160 snprintf(msg, sizeof(msg), "[%s]: %.*s", | |
| 161 p_conn->client_id, (int)p_msg->length, (char*)p_msg->data); | |
| 162 Seobeo_WebSocket_Server_Broadcast_Text(msg); | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 int main() | |
| 167 { | |
| 168 Seobeo_WebSocket_Server_Init(); | |
| 169 | |
| 170 // Register multiple WebSocket endpoints | |
| 171 Seobeo_WebSocket_Server_Register("/echo", Echo_Handler, NULL); | |
| 172 Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL); | |
| 173 | |
| 174 printf("Server started with:\n"); | |
| 175 printf(" ws://localhost:8080/echo - Echo server\n"); | |
| 176 printf(" ws://localhost:8080/chat - Chat room\n"); | |
| 177 | |
| 178 Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0); | |
| 179 return 0; | |
| 180 } | |
| 181 ``` | |
| 182 | |
| 183 ### 5. Custom User Data | |
| 184 | |
| 185 ```c | |
| 186 typedef struct { | |
| 187 int message_count; | |
| 188 char name[64]; | |
| 189 } ChatRoomData; | |
| 190 | |
| 191 void Chat_Handler_With_Data(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 192 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 193 { | |
| 194 ChatRoomData *p_data = (ChatRoomData*)p_user_data; | |
| 195 | |
| 196 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 197 { | |
| 198 p_data->message_count++; | |
| 199 | |
| 200 char message[2048]; | |
| 201 snprintf(message, sizeof(message), "[%s #%d]: %.*s", | |
| 202 p_data->name, p_data->message_count, | |
| 203 (int)p_msg->length, (char*)p_msg->data); | |
| 204 | |
| 205 Seobeo_WebSocket_Server_Broadcast_Text(message); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 int main() | |
| 210 { | |
| 211 ChatRoomData room_data = {0}; | |
| 212 strcpy(room_data.name, "Main Room"); | |
| 213 | |
| 214 Seobeo_WebSocket_Server_Init(); | |
| 215 Seobeo_WebSocket_Server_Register("/chat", Chat_Handler_With_Data, &room_data); | |
| 216 | |
| 217 Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0); | |
| 218 return 0; | |
| 219 } | |
| 220 ``` | |
| 221 | |
| 222 ### 6. Mixed HTTP and WebSocket Server | |
| 223 | |
| 224 ```c | |
| 225 // HTTP route handler | |
| 226 Seobeo_Request_Entry* Get_Status(Seobeo_Request_Entry *req, Dowa_Arena *arena) | |
| 227 { | |
| 228 Seobeo_Request_Entry *resp = NULL; | |
| 229 Dowa_HashMap_Push_Arena(resp, "status", "200", arena); | |
| 230 Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena); | |
| 231 Dowa_HashMap_Push_Arena(resp, "body", "{\"status\": \"online\"}", arena); | |
| 232 return resp; | |
| 233 } | |
| 234 | |
| 235 // WebSocket handler | |
| 236 void WS_Handler(Seobeo_WebSocket_Server_Connection *p_conn, | |
| 237 Seobeo_WebSocket_Message *p_msg, void *p_user_data) | |
| 238 { | |
| 239 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 240 Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data); | |
| 241 } | |
| 242 | |
| 243 int main() | |
| 244 { | |
| 245 // Initialize both HTTP and WebSocket routing | |
| 246 Seobeo_Router_Init(); | |
| 247 Seobeo_WebSocket_Server_Init(); | |
| 248 | |
| 249 // Register HTTP routes | |
| 250 Seobeo_Router_Register("GET", "/api/status", Get_Status); | |
| 251 | |
| 252 // Register WebSocket routes | |
| 253 Seobeo_WebSocket_Server_Register("/ws", WS_Handler, NULL); | |
| 254 | |
| 255 printf("Server started:\n"); | |
| 256 printf(" HTTP: http://localhost:8080/api/status\n"); | |
| 257 printf(" WebSocket: ws://localhost:8080/ws\n"); | |
| 258 | |
| 259 Seobeo_Web_Server_Start("./public", "8080", SEOBEO_MODE_FORK, 0); | |
| 260 return 0; | |
| 261 } | |
| 262 ``` | |
| 263 | |
| 264 ## Building | |
| 265 | |
| 266 ### Build the server example: | |
| 267 ```bash | |
| 268 bazel build //seobeo:websocket_server_example | |
| 269 ``` | |
| 270 | |
| 271 ### Run the server: | |
| 272 ```bash | |
| 273 bazel-bin/seobeo/websocket_server_example | |
| 274 ``` | |
| 275 | |
| 276 ### Test with a client: | |
| 277 You can test the server using the WebSocket client API or any WebSocket client tool: | |
| 278 ```bash | |
| 279 # Using websocat (install: cargo install websocat) | |
| 280 websocat ws://localhost:8080/echo | |
| 281 | |
| 282 # Using wscat (install: npm install -g wscat) | |
| 283 wscat -c ws://localhost:8080/chat | |
| 284 ``` | |
| 285 | |
| 286 ## Connection Structure | |
| 287 | |
| 288 ```c | |
| 289 struct Seobeo_WebSocket_Server_Connection_Struct { | |
| 290 Seobeo_Handle *p_handle; // Underlying socket handle | |
| 291 char *client_id; // Unique client identifier | |
| 292 boolean is_active; // Connection status | |
| 293 | |
| 294 // Fragment handling (internal) | |
| 295 uint8 *fragment_buffer; | |
| 296 size_t fragment_length; | |
| 297 size_t fragment_capacity; | |
| 298 Seobeo_WebSocket_Opcode fragment_opcode; | |
| 299 | |
| 300 Seobeo_WebSocket_Server_Connection *next; // Linked list | |
| 301 }; | |
| 302 ``` | |
| 303 | |
| 304 ## Protocol Details | |
| 305 | |
| 306 The server implementation follows RFC 6455: | |
| 307 | |
| 308 1. **Handshake**: Responds to HTTP Upgrade requests with proper Sec-WebSocket-Accept key | |
| 309 2. **Frame Format**: Server sends unmasked frames (RFC requirement) | |
| 310 3. **Frame Receiving**: Server receives and unmasks client frames | |
| 311 4. **Fragmentation**: Handles fragmented messages automatically | |
| 312 5. **Control Frames**: Proper handling of ping, pong, and close frames | |
| 313 6. **Broadcast**: Efficiently sends to all active connections | |
| 314 | |
| 315 ## Key Differences from Client | |
| 316 | |
| 317 | Feature | Client | Server | | |
| 318 |---------|--------|--------| | |
| 319 | Frame Masking | Masks outgoing frames | Does NOT mask outgoing frames | | |
| 320 | Connection | Initiates connection | Accepts connections | | |
| 321 | Upgrade | Sends upgrade request | Responds to upgrade request | | |
| 322 | Multiple Connections | Single connection | Manages multiple connections | | |
| 323 | Broadcast | N/A | Can broadcast to all clients | | |
| 324 | |
| 325 ## Coding Standards | |
| 326 | |
| 327 The implementation follows your specified coding standards: | |
| 328 - Naming: `Seobeo_WebSocket_Server_Init`, `Seobeo_WebSocket_Server_Send_Text` | |
| 329 - Two spaces for indentation | |
| 330 - New line before `{` unless it's a struct | |
| 331 - Single statement: no need for `{}` | |
| 332 | |
| 333 ## Integration with Existing Server | |
| 334 | |
| 335 The WebSocket server integrates seamlessly: | |
| 336 | |
| 337 1. **Automatic Detection**: The HTTP server automatically checks for WebSocket upgrade requests | |
| 338 2. **No Conflicts**: WebSocket and HTTP routes coexist peacefully | |
| 339 3. **Same Port**: WebSocket and HTTP share the same server port | |
| 340 4. **Unified Server**: Use `Seobeo_Web_Server_Start()` for everything | |
| 341 | |
| 342 ## Testing the Server | |
| 343 | |
| 344 ### JavaScript Client (Browser): | |
| 345 ```javascript | |
| 346 const ws = new WebSocket('ws://localhost:8080/echo'); | |
| 347 | |
| 348 ws.onopen = () => { | |
| 349 console.log('Connected!'); | |
| 350 ws.send('Hello, Server!'); | |
| 351 }; | |
| 352 | |
| 353 ws.onmessage = (event) => { | |
| 354 console.log('Received:', event.data); | |
| 355 }; | |
| 356 ``` | |
| 357 | |
| 358 ### Python Client: | |
| 359 ```python | |
| 360 import websocket | |
| 361 | |
| 362 ws = websocket.create_connection("ws://localhost:8080/chat") | |
| 363 ws.send("Hello from Python!") | |
| 364 print(ws.recv()) | |
| 365 ws.close() | |
| 366 ``` | |
| 367 | |
| 368 ## Performance Considerations | |
| 369 | |
| 370 1. **Fork Mode**: Each connection runs in a separate process (SEOBEO_MODE_FORK) | |
| 371 2. **Edge Mode**: Multi-threaded connections (SEOBEO_MODE_EDGE) | |
| 372 3. **Broadcast**: Iterates through all connections (O(n) complexity) | |
| 373 4. **Fragmentation**: Large messages are automatically fragmented (1MB chunks) | |
| 374 | |
| 375 ## Complete Feature Comparison | |
| 376 | |
| 377 | Feature | Implemented | | |
| 378 |---------|-------------| | |
| 379 | ✅ WebSocket upgrade handshake | Yes | | |
| 380 | ✅ Text messages | Yes | | |
| 381 | ✅ Binary messages | Yes | | |
| 382 | ✅ Message fragmentation | Yes | | |
| 383 | ✅ Ping/Pong | Yes | | |
| 384 | ✅ Close handshake | Yes | | |
| 385 | ✅ Multiple endpoints | Yes | | |
| 386 | ✅ Broadcast | Yes | | |
| 387 | ✅ Connection tracking | Yes | | |
| 388 | ✅ Integration with HTTP server | Yes | | |
| 389 | ✅ Custom user data per handler | Yes | | |
| 390 | |
| 391 The Seobeo WebSocket Server provides a complete, production-ready WebSocket implementation that works seamlessly with the existing HTTP server! |