WebSocket Protocol Implementation for Real-Time Bidirectional Communication
Overview
The NetBurner WebSocket implementation provides full-duplex communication channels over a single TCP connection, enabling real-time data exchange between clients and servers. WebSockets upgrade from HTTP connections and maintain persistent connections for efficient, low-latency bidirectional messaging.
Key features include:
- RFC 6455 compliant WebSocket protocol implementation
- Automatic connection upgrade from HTTP to WebSocket
- Support for text and binary data frames
- Built-in ping/pong for connection health monitoring
- Efficient buffering and flow control
- Support for both client and server roles
- Graceful connection closure with status codes
- Frame fragmentation for large messages
Typical WebSocket Workflow
Server Side:**
- Register a WebSocket upgrade handler using SetNewWSHandler()
- In the handler, call WSUpgrade() to upgrade the HTTP connection
- Use WSRead() and WSWrite() for bidirectional communication
- Send periodic WSPing() frames to monitor connection health
Close gracefully with close() when done
Client Side:**
- Call WebSocket::Connect() with server hostname/IP and resource path
- Receive a WebSocket file descriptor on success
- Use WSRead() and WSWrite() for communication
- Monitor connection with WSPing() and WSWaitForPingReply()
- Close gracefully with close() when done
WebSocket Frame Types
The WebSocket protocol supports several frame types:
- Text frames (WS_OP_TEXT): UTF-8 encoded text data
- Binary frames (WS_OP_BIN): Raw binary data
- Continuation frames (WS_OP_CONT): Fragments of a larger message
- Close frames (WS_OP_CLOSE): Graceful connection termination
- Ping frames (WS_OP_PING): Connection liveness check
- Pong frames (WS_OP_PONG): Response to ping frame
Most applications only need to work with text and binary frames, as control frames (ping, pong, close) are handled automatically by the implementation.
Buffering and Flow Control
The WebSocket implementation uses three internal buffers:
- RX Buffer: Incoming data from the TCP connection
- TX Buffer: Outgoing data waiting to be sent
- Control Buffer: Control frames (ping, pong, close)
Buffer sizes are configurable via compile-time constants:
- WS_BUFFER_SEGMENTS: Number of buffer segments (default: 4)
- WS_BUFFER_MIN: Minimum buffer threshold (default: 10)
Use GetWriteSpace() to check available TX buffer space before writing large amounts of data.
Connection Health Monitoring
WebSockets provide built-in mechanisms for connection health monitoring:
Ping/Pong Mechanism:**
Recommended practice:
- Send periodic pings (e.g., every 30-60 seconds)
- Measure round-trip time to detect network degradation
- Close connection if multiple pings timeout
- Use adaptive ping intervals based on application requirements
Graceful Connection Closure
WebSocket connections should be closed gracefully:
- Call close() on the WebSocket file descriptor
- The implementation sends a close frame with an optional status code
- The peer responds with a close frame
- The TCP connection is terminated
Common status codes (WS_StatusCode enum):
- WS_STAT_NORM_CLOSE (1000): Normal closure
- WS_STAT_GOING_AWAY (1001): Endpoint going away (server shutdown, browser navigation)
- WS_STAT_PROT_ERROR (1002): Protocol error
- WS_STAT_MSG_TOO_BIG (1009): Message too large to process
- WS_STAT_UNEXPECTED_COND (1011): Unexpected condition prevented request fulfillment
Thread Safety
The WebSocket implementation uses critical sections (OS_CRIT) to protect shared data structures. Multiple tasks can safely operate on different WebSocket connections. However, operations on the same WebSocket from multiple tasks require external synchronization.
Best practices:
- Use one task per WebSocket connection for simplicity
- If multiple tasks access the same WebSocket, use application-level locking
- The implementation handles internal buffer synchronization automatically
Resource Limits
The implementation has compile-time limits:
- Maximum WebSocket connections: WS_MAX_SOCKS (default: 4)
- Check available slots with GetFreeWebSocketCount()
- Each WebSocket consumes buffer memory and a file descriptor
- Consider system resources when determining WS_MAX_SOCKS value
Error Handling
WebSocket functions return negative values on errors:
- Check return values from all WebSocket functions
- WSRead() returns 0 for connection closure, negative for errors
- WSWrite() returns negative if connection is closed or buffer full
- Use errno or implementation-specific error codes for details
Common error scenarios:
- Connection closed by peer (WSRead returns 0 or negative)
- Write buffer full (WSWrite returns negative, check with GetWriteSpace())
- Invalid frame received (protocol error, connection closed)
- Timeout on blocking operations
Security Considerations
WebSocket security best practices:
- Use WSS (WebSocket Secure) over TLS for production deployments
- Validate Origin headers to prevent CSRF attacks
- Implement authentication before WSUpgrade()
- Rate-limit connections and messages to prevent DoS
- Validate and sanitize all received data
- Set maximum message size limits
- Use secure protocols for sensitive data
- Implement connection timeouts and limits
Performance Optimization
Tips for optimal WebSocket performance:
- Batch small messages when possible to reduce overhead
- Use binary frames for non-text data (more efficient than base64 text)
- Call Flush() explicitly when immediate transmission is needed
- Adjust WS_BUFFER_SEGMENTS for your message patterns
- Use GetWriteSpace() to avoid blocking on full buffers
- Consider message compression for large text payloads (if supported)
- Reuse connections rather than frequent connect/disconnect cycles
Debugging WebSocket Issues
Debug utilities:
- DumpSock(): Display detailed state of a single WebSocket
- DumpSockets(): Display state of all WebSocket connections
- Monitor buffer levels with GetWriteSpace()
- Track connection state with GetState()
- Use ping/pong to measure latency and detect connection issues
- Enable diagnostic output to track frame exchanges
Expand for Example Usage
Examples
- Example 1: Basic WebSocket Echo Server
#include <init.h>
#include <nbrtos.h>
#include <http.h>
#include <websockets.h>
const char *AppName = "WebSocket Echo Server";
void WebSocketEchoTask(void *pd) {
int wsFd = (int)(intptr_t)pd;
char buffer[512];
iprintf("WebSocket echo task started on fd %d\r\n", wsFd);
while (1) {
if (len > 0) {
buffer[len] = '\0';
iprintf("Received: %s\r\n", buffer);
if (WSWrite(wsFd, buffer, len) < 0) {
iprintf("Failed to send echo\r\n");
break;
}
} else if (len == 0) {
iprintf("WebSocket connection closed by peer\r\n");
break;
} else {
iprintf("WebSocket read error or timeout\r\n");
break;
}
}
iprintf("WebSocket echo task ended\r\n");
}
iprintf("WebSocket connection established\r\n");
OSSimpleTaskCreatewName(WebSocketEchoTask,
(void *)(intptr_t)sock,
"WSEcho");
return 0;
} else {
iprintf("WebSocket upgrade failed\r\n");
return -1;
}
}
void UserMain(void *pd) {
SetNewWSHandler("/ws", HandleWebSocketUpgrade);
iprintf("%s\r\n", AppName);
iprintf("WebSocket endpoint: ws://<device-ip>/ws\r\n");
while (1) {
}
}
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130
int close(int fd)
Close the specified file descriptor and free the associated resources.
int WSUpgrade(HTTP_Request *req, int sock)
Upgrade an HTTP connection to a WebSocket connection.
void StartHttp(uint16_t port, bool RunConfigMirror)
Start the HTTP web server. Further documentation in the Initialization section Initialization - Syste...
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
HTTP Request Structure.
Definition http.h:87
- Example 2: WebSocket Client Connection
#include <websockets.h>
void WebSocketClientTask(void *pd) {
"/api/v1/stream",
"chat",
80,
false);
if (wsFd < 0) {
iprintf("Failed to connect to WebSocket server\r\n");
return;
}
iprintf("Connected to WebSocket server (fd %d)\r\n", wsFd);
const char *msg = "Hello from NetBurner!";
if (WSWrite(wsFd, msg, strlen(msg)) < 0) {
iprintf("Failed to send message\r\n");
return;
}
char buffer[256];
if (len > 0) {
buffer[len] = '\0';
iprintf("Server response: %s\r\n", buffer);
}
iprintf("WebSocket client disconnected\r\n");
}
static int Connect(const char *host, const char *resource, const char *protocol, int portnum=80, bool useSSL=false)
Connect to a WebSocket server using hostname with optional protocol negotiation.
- Example 3: WebSocket with Connection Health Monitoring
void MonitoredWebSocketTask(void *pd) {
int wsFd = (int)(intptr_t)pd;
uint32_t lastPingTime = Secs;
uint32_t consecutiveFailures = 0;
const uint32_t PING_INTERVAL_SEC = 30;
const uint32_t MAX_FAILURES = 3;
char buffer[512];
while (1) {
if ((Secs - lastPingTime) >= PING_INTERVAL_SEC) {
uint32_t pingSent;
if (
WSPing(wsFd, 0, &pingSent) >= 0) {
uint32_t pongReceived;
iprintf("Ping successful, RTT: %lu ms\r\n", rtt);
}
consecutiveFailures = 0;
} else {
iprintf("Ping timeout\r\n");
consecutiveFailures++;
}
} else {
iprintf("Failed to send ping\r\n");
consecutiveFailures++;
}
lastPingTime = Secs;
if (consecutiveFailures >= MAX_FAILURES) {
iprintf("Connection appears dead, closing\r\n");
break;
}
}
if (len > 0) {
buffer[len] = '\0';
iprintf("Received: %s\r\n", buffer);
} else if (len < 0) {
iprintf("Read error or connection closed\r\n");
break;
}
}
}
int WSPing(int fd, uint32_t len, uint32_t *sentTick)
Send a WebSocket ping frame to test connection liveness and measure round-trip time.
int WSWaitForPingReply(int fd, uint32_t timeout)
Wait for a pong reply to a previously sent ping with a specified timeout.
int WSGetPingReplyTick(int fd, uint32_t *replyTick)
Retrieve the timestamp of the most recent pong reply received on a WebSocket connection.
- Example 4: WebSocket Broadcast Server
#define MAX_WS_CLIENTS 10
struct WSClient {
int fd;
bool active;
};
WSClient wsClients[MAX_WS_CLIENTS];
OS_CRIT wsClientsCrit;
bool AddWSClient(int fd) {
for (int i = 0; i < MAX_WS_CLIENTS; i++) {
if (!wsClients[i].active) {
wsClients[i].fd = fd;
wsClients[i].active = true;
OSCritLeave(&wsClientsCrit);
return true;
}
}
OSCritLeave(&wsClientsCrit);
return false;
}
void RemoveWSClient(int fd) {
for (int i = 0; i < MAX_WS_CLIENTS; i++) {
if (wsClients[i].active && wsClients[i].fd == fd) {
wsClients[i].active = false;
break;
}
}
OSCritLeave(&wsClientsCrit);
}
void BroadcastMessage(const char *msg, int len) {
for (int i = 0; i < MAX_WS_CLIENTS; i++) {
if (wsClients[i].active) {
if (WSWrite(wsClients[i].fd, msg, len) < 0) {
iprintf("Failed to send to client %d\r\n", wsClients[i].fd);
}
}
}
OSCritLeave(&wsClientsCrit);
}
void WSClientTask(void *pd) {
int wsFd = (int)(intptr_t)pd;
char buffer[512];
if (!AddWSClient(wsFd)) {
iprintf("Too many clients, rejecting connection\r\n");
return;
}
iprintf("Client connected: fd %d\r\n", wsFd);
while (1) {
if (len > 0) {
buffer[len] = '\0';
iprintf("Broadcasting message from fd %d: %s\r\n", wsFd, buffer);
BroadcastMessage(buffer, len);
} else {
iprintf("Client disconnected: fd %d\r\n", wsFd);
break;
}
}
RemoveWSClient(wsFd);
}
OSSimpleTaskCreatewName(WSClientTask,
MAIN_PRIO - 1,
(void *)(intptr_t)sock, "WSClient");
return 0;
}
return -1;
}
- Example 5: WebSocket with Authentication and Options
#include <http.h>
#include <websockets.h>
const char *token = GetRequestParam(req, "token");
if (token == NULL) {
return false;
}
return (strcmp(token, "secret_key_12345") == 0);
}
void AuthenticatedWSTask(void *pd) {
int wsFd = (int)(intptr_t)pd;
char buffer[1024];
iprintf("Authenticated WebSocket connection established\r\n");
const char *welcome = "{\"type\":\"welcome\",\"message\":\"Connected to secure endpoint\"}";
WSWrite(wsFd, welcome, strlen(welcome));
while (1) {
if (len > 0) {
buffer[len] = '\0';
iprintf("Secure message: %s\r\n", buffer);
char response[1024];
snprintf(response, sizeof(response),
"{\"type\":\"ack\",\"received\":%d}", len);
WSWrite(wsFd, response, strlen(response));
} else if (len == 0) {
iprintf("Client disconnected gracefully\r\n");
break;
} else {
iprintf("Error or timeout\r\n");
break;
}
}
}
if (!ValidateAuth(req)) {
iprintf("Authentication failed\r\n");
return -1;
}
OSSimpleTaskCreatewName(AuthenticatedWSTask,
MAIN_PRIO - 1,
(void *)(intptr_t)sock, "WSAuth");
return 0;
}
return -1;
}
int writestring(int fd, const char *str)
Write a null terminated ascii string to the stream associated with a file descriptor (fd)....
#define WS_SO_TEXT
Socket option flag to enable text frame mode (vs binary mode)
Definition websockets.h:72
| Function | Description |
| WSUpgrade() | Upgrade HTTP connection to WebSocket |
| WSRead() | Read data from WebSocket (ws_read wrapper) |
| WSWrite() | Write data to WebSocket (ws_write wrapper) |
| WSPing() | Send ping frame for connection health check |
| WSWaitForPingReply() | Wait for pong response with timeout |
| WSGetPingReplyTick() | Get timestamp of last pong for latency measurement |
| SetNewWSHandler() | Register WebSocket upgrade handler |
| NB::WebSocket::Connect() | Create client WebSocket connection |
| close() | Close WebSocket connection |
Configuration and Return Code Definitions
#define WS_BUFFER_SEGMENTS 4
#define WS_MAX_SOCKS 4
#define WS_BUFFER_MIN 10
#define WS_ACCUM_DLY ((TICKS_PER_SECOND / 4) + 1)
#define WS_FLUSH_TIMEOUT 0
#define WS_FIN_BIT 0x80
#define WS_OP_CONT 0x0
#define WS_OP_TEXT 0x1
#define WS_OP_BIN 0x2
#define WS_OP_CLOSE 0x8
#define WS_OP_PING 0x9
#define WS_OP_PONG 0xA
#define WS_SO_TEXT 0x8
Number of buffer segments allocated for each WebSocket connection.
Each WebSocket connection uses segmented buffers for efficient memory management and flow control. This value determines how many segments are allocated for the receive (RX), transmit (TX), and control buffers.
#define WS_BUFFER_SEGMENTS 4
Default Value:** 4 segments per buffer
Memory Impact:**
- Note
- This is a compile-time constant and cannot be changed at runtime
-
Changing this value requires recompiling the WebSocket library
- See also
- WS_MAX_SOCKS
-
WS_BUFFER_MIN
Maximum number of simultaneous WebSocket connections supported.
This defines the compile-time limit for concurrent WebSocket connections that can be active on the device. Each connection consumes memory for buffers and internal state management.
define WS_MAX_SOCKS 4
Default Value:** 4 connections
Resource Considerations:**
- Each connection uses approximately (WS_BUFFER_SEGMENTS x 3) buffer segments
- Each connection requires a file descriptor and internal state structure
Total memory usage scales linearly with WS_MAX_SOCKS
Tuning Guidelines:**
- Increase for servers handling multiple simultaneous clients
- Decrease to conserve memory on resource-constrained devices
- Use GetFreeWebSocketCount() to check available connection slots at runtime
Consider total system file descriptors and memory when adjusting
Example Memory Calculation:**
- Note
- This is a compile-time constant and cannot be changed at runtime
-
Exceeding this limit will cause new connection attempts to fail
-
Use GetFreeWebSocketCount() to monitor available slots
- See also
- WS_BUFFER_SEGMENTS
-
GetFreeWebSocketCount()
Expand for Example Usage
Examples
- Example: Checking Available Connection Slots
if (freeSlots <= 0) {
iprintf("No WebSocket slots available (%d/%d used)\r\n",
writestring(sock,
"HTTP/1.1 503 Service Unavailable\r\n");
writestring(sock,
"Maximum WebSocket connections reached");
return -1;
}
iprintf("Accepting WebSocket connection (%d slots remaining)\r\n", freeSlots);
OSSimpleTaskCreatewName(WebSocketTask,
MAIN_PRIO - 1,
(void *)(intptr_t)sock, "WSTask");
return 0;
}
return -1;
}
static int GetFreeWebSocketCount()
Get the number of available WebSocket connection slots.
#define WS_MAX_SOCKS
Maximum number of simultaneous WebSocket connections supported.
Definition websockets.h:59
Minimum buffer space threshold before triggering buffer management actions.
This defines the minimum number of bytes that should remain available in the transmit buffer before the WebSocket implementation takes buffer management actions, such as flushing pending data or blocking writes.
define WS_BUFFER_MIN 10
Default Value:** 10 bytes
Purpose:**
- Note
- This is a compile-time constant affecting buffer management behavior
-
Setting too high may reduce effective buffer capacity
-
Setting too low may increase risk of buffer overrun
- See also
- GetWriteSpace()
-
Flush()
Accumulation delay for batching small writes before transmission.
Time delay (in system ticks) that the WebSocket implementation waits to accumulate small writes before sending them as a single frame. This reduces protocol overhead by batching multiple small writes together.
define WS_ACCUM_DLY ((TICKS_PER_SECOND / 4) + 1)
Default Value:** (TICKS_PER_SECOND / 4) + 1 (approximately 250ms + 1 tick)
Purpose:**
- Note
- Value is in system ticks (convert using TICKS_PER_SECOND)
-
Explicit Flush() calls override this delay
- See also
- Flush()
-
TICKS_PER_SECOND
-
WS_FLUSH_TIMEOUT
Timeout value for flush operations (in system ticks).
Specifies the maximum time to wait when flushing buffered WebSocket data. A value of 0 indicates non-blocking flush operations.
define WS_FLUSH_TIMEOUT 0
Default Value:** 0 (non-blocking)
Behavior:**
- Note
- Value is in system ticks
-
Most applications should use the default non-blocking behavior
- See also
- Flush()
-
WS_ACCUM_DLY
WebSocket frame FIN bit mask indicating final fragment.
The FIN bit is set in the first byte of a WebSocket frame to indicate whether this is the final fragment of a message. According to RFC 6455, the FIN bit is the most significant bit (bit 7) of the first frame byte.
define WS_FIN_BIT 0x80
Bit Position:** 0x80 (bit 7 set)
Frame Fragmentation:**
- FIN = 1: This is the final (or only) fragment of the message
FIN = 0: More fragments follow this one
Usage:**
- Set on single-frame messages (most common case)
- Set on the last frame of a fragmented message
Clear on continuation frames (except the last one)
Message Fragmentation Example:**
- Note
- This is typically handled automatically by the WebSocket implementation
-
Fragmentation allows sending large messages without buffering entirely
- See also
- WS_OP_CONT
-
WS_OP_TEXT
-
WS_OP_BIN
WebSocket continuation frame opcode.
Opcode value for continuation frames, which carry subsequent fragments of a message that was started with a TEXT or BINARY frame. Continuation frames allow large messages to be sent in multiple fragments.
define WS_OP_CONT 0x0
Opcode Value:** 0x0
Frame Sequence:**
- First frame: TEXT or BINARY opcode with FIN=0
- Middle frames: CONT opcode with FIN=0
Final frame: CONT opcode with FIN=1
Rules:**
- Must follow a TEXT or BINARY frame with FIN=0
- Cannot start a new message (must be part of fragmented message)
- All fragments must be same type (all text or all binary)
- Control frames (PING, PONG, CLOSE) cannot be fragmented
- Note
- The WebSocket implementation typically handles fragmentation automatically
-
Interleaving control frames between fragments is allowed by RFC 6455
- See also
- WS_OP_TEXT
-
WS_OP_BIN
-
WS_FIN_BIT
WebSocket text frame opcode for UTF-8 encoded text data.
Opcode value for frames containing UTF-8 encoded text data. Text frames are used for sending human-readable text messages and JSON data.
#define WS_OP_TEXT 0x1
Opcode Value:** 0x1
Characteristics:**
- Note
- Payload validation ensures UTF-8 compliance (per RFC 6455)
-
For non-text data, use WS_OP_BIN for better performance
- See also
- WS_OP_BIN
-
WS_OP_CONT
-
WS_SO_TEXT
Expand for Example Usage
Examples
- Example: Sending Text vs Binary Data
int wsFd = ...;
const char *jsonMsg = "{\"temp\":25.5,\"humidity\":60}";
WSWrite(wsFd, jsonMsg, strlen(jsonMsg));
uint8_t binaryData[100];
WSWrite(wsFd, (char*)binaryData, sizeof(binaryData));
WebSocket binary frame opcode for raw binary data.
Opcode value for frames containing arbitrary binary data. Binary frames are more efficient than text frames for non-textual data as they bypass UTF-8 validation and encoding overhead.
#define WS_OP_BIN 0x2
Opcode Value:** 0x2
Characteristics:**
- Payload can contain any byte sequence
- No UTF-8 validation performed
- More efficient for images, files, compressed data, etc.
Commonly used for protocol buffers, MessagePack, CBOR
Use Cases:**
- Binary protocols (Protocol Buffers, MessagePack, BSON)
- Image and media data
- Compressed data
- Encrypted payloads
- Raw sensor data
Any non-text binary format
vs Text Frames:**
- BINARY: No encoding overhead, any byte values, faster
- TEXT: UTF-8 validated, human-readable, JSON-compatible
- Note
- More efficient than Base64-encoding binary data in text frames
-
No content validation means any byte sequence is valid
- See also
- WS_OP_TEXT
-
WS_OP_CONT
Expand for Example Usage
Examples
- Example 1: Sending Binary Sensor Data
struct SensorReading {
uint32_t timestamp;
float temperature;
float humidity;
uint16_t pressure;
} __attribute__((packed));
void SendSensorData(int wsFd) {
SensorReading reading;
reading.timestamp = Secs;
reading.temperature = 25.5f;
reading.humidity = 60.0f;
reading.pressure = 1013;
WSWrite(wsFd, (char*)&reading, sizeof(reading));
}
- Example 2: Binary vs Text Efficiency
void CompareTextVsBinary(int wsFd) {
char jsonBuf[100];
int jsonLen = snprintf(jsonBuf, sizeof(jsonBuf),
"{\"ts\":%lu,\"t\":%.1f,\"h\":%.1f,\"p\":%d}",
Secs, 25.5f, 60.0f, 1013);
WSWrite(wsFd, jsonBuf, jsonLen);
iprintf("Text frame: %d bytes\r\n", jsonLen);
SensorReading reading = {Secs, 25.5f, 60.0f, 1013};
WSWrite(wsFd, (char*)&reading, sizeof(reading));
iprintf("Binary frame: %d bytes\r\n", sizeof(reading));
}
- Example 3: Sending Image Data
void SendImageFrame(int wsFd, uint8_t *imageData, uint32_t imageSize) {
if (GetWriteSpace() < imageSize) {
iprintf("Insufficient buffer space\r\n");
return;
}
int sent = WSWrite(wsFd, (char*)imageData, imageSize);
if (sent == imageSize) {
iprintf("Sent %u byte image\r\n", imageSize);
} else {
iprintf("Image send failed\r\n");
}
}
WebSocket close frame opcode for connection termination.
Opcode value for close frames, which initiate graceful connection closure. Close frames may include a status code and optional reason string explaining why the connection is being closed.
define WS_OP_CLOSE 0x8
Opcode Value:** 0x8
Closure Handshake:**
- One endpoint sends CLOSE frame with optional status code
- Peer responds with CLOSE frame
Both endpoints close the underlying TCP connection
Close Frame Format:**
- Note
- Close frames are automatically handled by the WebSocket implementation
-
After sending/receiving CLOSE, no more data frames should be sent
-
Always close gracefully rather than abruptly terminating TCP connection
- See also
- WS_STAT_NORM_CLOSE
-
WS_STAT_GOING_AWAY
WebSocket ping frame opcode for connection liveness check.
Opcode value for ping frames, which test connection liveness and measure round-trip time. The recipient must respond with a pong frame containing the same payload.
define WS_OP_PING 0x9
Opcode Value:** 0x9
Ping/Pong Protocol:**
- Note
- Use WSPing() function rather than manually constructing frames
-
Automatic pong responses are handled by the implementation
- See also
- WS_OP_PONG
-
WSPing()
-
WSWaitForPingReply()
-
WSGetPingReplyTick()
Expand for Example Usage
Examples
- Example: Connection Health Monitoring
void MonitorWebSocketHealth(int wsFd) {
uint32_t pingSent, pongReceived;
if (
WSPing(wsFd, 0, &pingSent) >= 0) {
iprintf("Connection alive, RTT: %lu ms\r\n", rtt);
}
} else {
iprintf("Ping timeout - connection may be dead\r\n");
}
}
}
WebSocket pong frame opcode for ping response.
Opcode value for pong frames, which are sent in response to ping frames. Pong frames must echo back the exact payload received in the corresponding ping frame.
#define WS_OP_PONG 0xA
Opcode Value:** 0xA
Ping/Pong Protocol:**
- Note
- Pong responses are typically handled automatically by the WebSocket stack
-
Applications rarely need to manually send pong frames
-
Use WSPong() if manual pong transmission is needed
- See also
- WS_OP_PING
-
WSPing()
-
WSWaitForPingReply()
WebSocket socket option to enable text frame mode.
Socket option flag to configure a WebSocket connection to send data as text frames (WS_OP_TEXT) rather than binary frames (WS_OP_BIN). This affects the opcode used for outgoing data frames.
#define WS_SO_TEXT 0x8
Option Value:** 0x8
Behavior:**
- When set: Outgoing frames use WS_OP_TEXT opcode
When clear: Outgoing frames use WS_OP_BIN opcode
Text Frame Requirements:**
- Payload must be valid UTF-8 encoded text
- Implementation may validate UTF-8 encoding
Use for JSON, XML, HTML, or plain text data
Functions:**
- ws_setoption(fd, WS_SO_TEXT): Enable text mode
- ws_clroption(fd, WS_SO_TEXT): Enable binary mode
ws_getoption(fd): Check current option state
When to Use Text Mode:**
- Sending JSON, XML, or other text-based protocols
- Human-readable messages
- When client/server expects text frames
For maximum compatibility with web browsers
When to Use Binary Mode:**
- Raw sensor data or binary protocols
- Image, audio, video data
- Compressed or encrypted payloads
- Maximum efficiency (no UTF-8 overhead)
- Note
- Default mode may vary; set explicitly for predictable behavior
-
Mode can be changed dynamically during connection lifetime
- See also
- WS_OP_TEXT
-
WS_OP_BIN
-
ws_setoption()
-
ws_clroption()
-
ws_getoption()
Expand for Example Usage
Examples
- Example 1: Setting Text Mode for JSON
void SendJSONMessage(int wsFd, const char *jsonStr) {
int sent = WSWrite(wsFd, jsonStr, strlen(jsonStr));
if (sent > 0) {
iprintf("Sent JSON: %s\r\n", jsonStr);
}
}
- Example 2: Switching Between Text and Binary
void SendMixedData(int wsFd) {
const char *textMsg = "{\"type\":\"status\",\"value\":\"ready\"}";
WSWrite(wsFd, textMsg, strlen(textMsg));
struct {
uint32_t timestamp;
float temperature;
} sensorData = {Secs, 25.5f};
WSWrite(wsFd, (char*)&sensorData, sizeof(sensorData));
const char *ack = "{\"type\":\"ack\"}";
WSWrite(wsFd, ack, strlen(ack));
}
- Example 3: Checking Current Mode
void DisplayWebSocketMode(int wsFd) {
int options = ws_getoption(wsFd);
iprintf("WebSocket fd %d: TEXT mode (UTF-8)\r\n", wsFd);
} else {
iprintf("WebSocket fd %d: BINARY mode\r\n", wsFd);
}
}
- Example 4: Application-Specific Protocol
enum MessageType {
MSG_TEXT_JSON,
MSG_BINARY_PROTOBUF,
MSG_BINARY_IMAGE
};
void SendMessage(int wsFd, MessageType type, const void *data, int len) {
switch (type) {
case MSG_TEXT_JSON:
iprintf("Sending as TEXT frame\r\n");
break;
case MSG_BINARY_PROTOBUF:
case MSG_BINARY_IMAGE:
iprintf("Sending as BINARY frame\r\n");
break;
}
int sent = WSWrite(wsFd, (const char*)data, len);
iprintf("Sent %d bytes\r\n", sent);
}
- Example 5: WebSocket Mode Configuration Helper
void ConfigureJSONWebSocket(int wsFd) {
int opts = ws_getoption(wsFd);
iprintf("WebSocket configured for JSON (text mode)\r\n");
} else {
iprintf("ERROR: Failed to set text mode\r\n");
}
}
void ConfigureBinaryWebSocket(int wsFd) {
int opts = ws_getoption(wsFd);
iprintf("WebSocket configured for binary protocol\r\n");
} else {
iprintf("ERROR: Failed to clear text mode\r\n");
}
}