Mercurial
comparison seobeo/docs/web_socket_client.md @ 120:cbbf78b17cfa
[Seobeo][Websocket] Created Web socket client logic.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 08 Jan 2026 03:19:59 -0800 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 119:c39582f937e5 | 120:cbbf78b17cfa |
|---|---|
| 1 # Seobeo WebSocket Client - Usage Guide | |
| 2 | |
| 3 A clean, easy-to-use WebSocket client library following RFC 6455. | |
| 4 | |
| 5 ## Features | |
| 6 | |
| 7 1. **WebSocket Protocol Support**: Full RFC 6455 WebSocket implementation | |
| 8 2. **Text and Binary Messages**: Send/receive both text and binary data | |
| 9 3. **Automatic Ping/Pong**: Automatic pong responses to ping frames | |
| 10 4. **Message Fragmentation**: Handles fragmented messages automatically | |
| 11 5. **Secure WebSocket**: Supports both `ws://` and `wss://` (WebSocket over TLS) | |
| 12 6. **Clean API**: Simple, intuitive API following seobeo coding standards | |
| 13 | |
| 14 ## API Overview | |
| 15 | |
| 16 ### Core Functions | |
| 17 | |
| 18 ```c | |
| 19 // Connect to WebSocket server | |
| 20 Seobeo_WebSocket *Seobeo_WebSocket_Connect(const char *url); | |
| 21 | |
| 22 // Send messages | |
| 23 int32 Seobeo_WebSocket_Send_Text(Seobeo_WebSocket *p_ws, const char *text); | |
| 24 int32 Seobeo_WebSocket_Send_Binary(Seobeo_WebSocket *p_ws, const uint8 *data, size_t length); | |
| 25 | |
| 26 // Send control frames | |
| 27 int32 Seobeo_WebSocket_Send_Ping(Seobeo_WebSocket *p_ws, const char *payload); | |
| 28 int32 Seobeo_WebSocket_Send_Pong(Seobeo_WebSocket *p_ws, const char *payload); | |
| 29 | |
| 30 // Receive messages | |
| 31 Seobeo_WebSocket_Message *Seobeo_WebSocket_Receive(Seobeo_WebSocket *p_ws); | |
| 32 | |
| 33 // Close connection | |
| 34 int32 Seobeo_WebSocket_Close(Seobeo_WebSocket *p_ws, uint16 code, const char *reason); | |
| 35 | |
| 36 // Clean up | |
| 37 void Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg); | |
| 38 void Seobeo_WebSocket_Destroy(Seobeo_WebSocket *p_ws); | |
| 39 ``` | |
| 40 | |
| 41 ### Message Opcodes | |
| 42 | |
| 43 ```c | |
| 44 typedef enum { | |
| 45 SEOBEO_WS_OPCODE_CONTINUATION = 0x0, // Continuation frame | |
| 46 SEOBEO_WS_OPCODE_TEXT = 0x1, // Text message | |
| 47 SEOBEO_WS_OPCODE_BINARY = 0x2, // Binary message | |
| 48 SEOBEO_WS_OPCODE_CLOSE = 0x8, // Close connection | |
| 49 SEOBEO_WS_OPCODE_PING = 0x9, // Ping frame | |
| 50 SEOBEO_WS_OPCODE_PONG = 0xA // Pong frame | |
| 51 } Seobeo_WebSocket_Opcode; | |
| 52 ``` | |
| 53 | |
| 54 ### Connection States | |
| 55 | |
| 56 ```c | |
| 57 typedef enum { | |
| 58 SEOBEO_WS_STATE_CONNECTING = 0, // Handshake in progress | |
| 59 SEOBEO_WS_STATE_OPEN, // Connection established | |
| 60 SEOBEO_WS_STATE_CLOSING, // Close handshake initiated | |
| 61 SEOBEO_WS_STATE_CLOSED // Connection closed | |
| 62 } Seobeo_WebSocket_State; | |
| 63 ``` | |
| 64 | |
| 65 ## Examples | |
| 66 | |
| 67 ### 1. Simple Text Echo | |
| 68 | |
| 69 ```c | |
| 70 // Connect to WebSocket server | |
| 71 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://echo.websocket.org"); | |
| 72 if (!p_ws) | |
| 73 { | |
| 74 printf("Failed to connect\n"); | |
| 75 return; | |
| 76 } | |
| 77 | |
| 78 // Send text message | |
| 79 const char *message = "Hello, WebSocket!"; | |
| 80 Seobeo_WebSocket_Send_Text(p_ws, message); | |
| 81 | |
| 82 // Receive echo response | |
| 83 Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); | |
| 84 if (p_msg && p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 85 { | |
| 86 printf("Received: %.*s\n", (int)p_msg->length, (char*)p_msg->data); | |
| 87 Seobeo_WebSocket_Message_Destroy(p_msg); | |
| 88 } | |
| 89 | |
| 90 // Close connection | |
| 91 Seobeo_WebSocket_Close(p_ws, 1000, "Normal closure"); | |
| 92 Seobeo_WebSocket_Destroy(p_ws); | |
| 93 ``` | |
| 94 | |
| 95 ### 2. Binary Data Transfer | |
| 96 | |
| 97 ```c | |
| 98 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com/data"); | |
| 99 | |
| 100 // Send binary data | |
| 101 uint8 data[] = {0x01, 0x02, 0x03, 0xAA, 0xBB, 0xCC}; | |
| 102 Seobeo_WebSocket_Send_Binary(p_ws, data, sizeof(data)); | |
| 103 | |
| 104 // Receive binary response | |
| 105 Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); | |
| 106 if (p_msg && p_msg->opcode == SEOBEO_WS_OPCODE_BINARY) | |
| 107 { | |
| 108 printf("Received %zu bytes\n", p_msg->length); | |
| 109 // Process binary data... | |
| 110 Seobeo_WebSocket_Message_Destroy(p_msg); | |
| 111 } | |
| 112 | |
| 113 Seobeo_WebSocket_Destroy(p_ws); | |
| 114 ``` | |
| 115 | |
| 116 ### 3. Chat Application | |
| 117 | |
| 118 ```c | |
| 119 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://chat.example.com"); | |
| 120 | |
| 121 // Send chat message | |
| 122 Seobeo_WebSocket_Send_Text(p_ws, "Hello everyone!"); | |
| 123 | |
| 124 // Continuous receive loop | |
| 125 while (1) | |
| 126 { | |
| 127 Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); | |
| 128 if (p_msg) | |
| 129 { | |
| 130 if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) | |
| 131 { | |
| 132 printf("Chat: %.*s\n", (int)p_msg->length, (char*)p_msg->data); | |
| 133 } | |
| 134 Seobeo_WebSocket_Message_Destroy(p_msg); | |
| 135 } | |
| 136 | |
| 137 usleep(10000); // 10ms sleep to avoid busy waiting | |
| 138 } | |
| 139 | |
| 140 Seobeo_WebSocket_Destroy(p_ws); | |
| 141 ``` | |
| 142 | |
| 143 ### 4. Ping/Pong Keep-Alive | |
| 144 | |
| 145 ```c | |
| 146 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://api.example.com"); | |
| 147 | |
| 148 // Send ping to keep connection alive | |
| 149 Seobeo_WebSocket_Send_Ping(p_ws, "keep-alive"); | |
| 150 | |
| 151 // Server will automatically receive pong responses | |
| 152 // (pong responses are handled internally) | |
| 153 | |
| 154 Seobeo_WebSocket_Destroy(p_ws); | |
| 155 ``` | |
| 156 | |
| 157 ### 5. Handling Different Message Types | |
| 158 | |
| 159 ```c | |
| 160 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com"); | |
| 161 | |
| 162 while (1) | |
| 163 { | |
| 164 Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); | |
| 165 if (p_msg) | |
| 166 { | |
| 167 switch (p_msg->opcode) | |
| 168 { | |
| 169 case SEOBEO_WS_OPCODE_TEXT: | |
| 170 printf("Text: %.*s\n", (int)p_msg->length, (char*)p_msg->data); | |
| 171 break; | |
| 172 | |
| 173 case SEOBEO_WS_OPCODE_BINARY: | |
| 174 printf("Binary: %zu bytes\n", p_msg->length); | |
| 175 break; | |
| 176 | |
| 177 default: | |
| 178 printf("Unknown opcode: 0x%X\n", p_msg->opcode); | |
| 179 break; | |
| 180 } | |
| 181 | |
| 182 Seobeo_WebSocket_Message_Destroy(p_msg); | |
| 183 } | |
| 184 | |
| 185 usleep(10000); | |
| 186 } | |
| 187 | |
| 188 Seobeo_WebSocket_Destroy(p_ws); | |
| 189 ``` | |
| 190 | |
| 191 ### 6. Graceful Shutdown | |
| 192 | |
| 193 ```c | |
| 194 Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com"); | |
| 195 | |
| 196 // Do work... | |
| 197 | |
| 198 // Close with custom status code and reason | |
| 199 Seobeo_WebSocket_Close(p_ws, 1000, "Client shutting down"); | |
| 200 Seobeo_WebSocket_Destroy(p_ws); | |
| 201 ``` | |
| 202 | |
| 203 ## WebSocket Close Codes | |
| 204 | |
| 205 Common close status codes (RFC 6455): | |
| 206 | |
| 207 - **1000**: Normal closure | |
| 208 - **1001**: Going away (e.g., server shutdown, browser navigation) | |
| 209 - **1002**: Protocol error | |
| 210 - **1003**: Unsupported data type | |
| 211 - **1006**: Abnormal closure (no close frame received) | |
| 212 - **1007**: Invalid frame payload data | |
| 213 - **1008**: Policy violation | |
| 214 - **1009**: Message too big | |
| 215 - **1010**: Mandatory extension missing | |
| 216 - **1011**: Internal server error | |
| 217 | |
| 218 ## Building | |
| 219 | |
| 220 ### Build the library: | |
| 221 ```bash | |
| 222 bazel build //seobeo:seobeo_client | |
| 223 ``` | |
| 224 | |
| 225 ### Build and run the WebSocket test: | |
| 226 ```bash | |
| 227 bazel test //seobeo:seobeo_websocket_test | |
| 228 ``` | |
| 229 | |
| 230 ## Message Structure | |
| 231 | |
| 232 ```c | |
| 233 typedef struct { | |
| 234 Seobeo_WebSocket_Opcode opcode; // Message type | |
| 235 uint8 *data; // Message payload | |
| 236 size_t length; // Payload length | |
| 237 boolean is_final; // Final fragment flag | |
| 238 } Seobeo_WebSocket_Message; | |
| 239 ``` | |
| 240 | |
| 241 ## Protocol Details | |
| 242 | |
| 243 The implementation follows RFC 6455: | |
| 244 | |
| 245 1. **Handshake**: HTTP Upgrade request with `Sec-WebSocket-Key` | |
| 246 2. **Frame Format**: Proper FIN, opcode, mask, and payload length handling | |
| 247 3. **Masking**: All client-to-server frames are masked (required by RFC) | |
| 248 4. **Fragmentation**: Handles fragmented messages across multiple frames | |
| 249 5. **Control Frames**: Proper handling of ping, pong, and close frames | |
| 250 6. **TLS Support**: Supports secure WebSocket (wss://) via OpenSSL | |
| 251 | |
| 252 ## Coding Standards | |
| 253 | |
| 254 The implementation follows your specified coding standards: | |
| 255 - Naming: `Seobeo_WebSocket_Connect`, `Seobeo_WebSocket_Send_Text` | |
| 256 - Two spaces for indentation | |
| 257 - New line before `{` unless it's a struct | |
| 258 - Single statement: no need for `{}` | |
| 259 | |
| 260 ## Features | |
| 261 | |
| 262 ✅ WebSocket handshake (HTTP Upgrade) | |
| 263 ✅ Text and binary message support | |
| 264 ✅ Automatic masking for client frames | |
| 265 ✅ Message fragmentation handling | |
| 266 ✅ Ping/Pong frames | |
| 267 ✅ Close handshake | |
| 268 ✅ TLS/SSL support (wss://) | |
| 269 ✅ Non-blocking receive | |
| 270 ✅ Proper memory management with arenas |