NetBurner 3.5.6
PDF Version
NB::WebSocket Class Reference

WebSocket connection management class. More...

#include <websockets.h>

Public Member Functions

int WriteData (const char *buf, int nbytes)
 Write data to the WebSocket connection.
 
int ReadData (char *buf, int nbytes)
 Read data from the WebSocket connection.
 
void Flush ()
 Flush buffered transmit data to the network.
 
void Pong (uint32_t len)
 Send a pong frame in response to a ping.
 
WS_State GetState ()
 Get the current connection state of the WebSocket.
 
int Ping (uint32_t len, uint32_t *sentTick)
 Send a WebSocket ping frame and record the timestamp.
 
int WaitForPingReply (uint32_t timeout)
 Block waiting for a pong reply to a previously sent ping.
 
int GetPingReplyTick (uint32_t *replyTick)
 Get the tick count when the last pong was received.
 
int setoption (int option)
 Set a socket option on this WebSocket connection.
 
int clroption (int option)
 Clear a socket option on this WebSocket connection.
 
int getoption ()
 Get the current socket options bitmap.
 
int GetWriteSpace ()
 Get available transmit buffer space in bytes.
 
void DumpSock ()
 Dump detailed state information for this WebSocket connection.
 

Static Public Member Functions

static int ws_readwto (int fd, char *buf, int nbytes, int timeout)
 Read data from a WebSocket connection with timeout.
 
static int ws_read (int fd, char *buf, int nbytes)
 Read data from a WebSocket connection.
 
static int ws_write (int fd, const char *buf, int nbytes)
 Write data to a WebSocket connection.
 
static int ws_externalclose (int fd)
 Close a WebSocket connection from an external caller.
 
static void ws_flush (int fd)
 Flush the transmit buffer of a WebSocket connection.
 
static int ws_setoption (int fd, int option)
 Set a socket option on a WebSocket connection.
 
static int ws_clroption (int fd, int option)
 Clear a socket option on a WebSocket connection.
 
static int ws_getoption (int fd)
 Get the current socket options for a WebSocket connection.
 
static WebSocketGetWebSocketRecord (int fd)
 Find a WebSocket record by its file descriptor.
 
static int promote_tcp_ws (int tcp_fd)
 Promote a TCP socket to a WebSocket connection after successful handshake.
 
static void ws_read_notify (int fd)
 TCP read notification callback invoked when data is available on the WebSocket.
 
static void ws_write_notify (int fd)
 TCP write notification callback invoked when buffer space becomes available.
 
static void GetFlushTime ()
 Updates the global flush timing for WebSocket message delivery.
 
static void RunSkippedCallBack ()
 Executes the callback function when a scheduled run is skipped.
 
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.
 
static int Connect (const char *host, const char *resource, int portnum=80, bool useSSL=false)
 Connect to a WebSocket server using hostname without protocol negotiation.
 
static int Connect (IPADDR host, const char *resource, const char *protocol, int portnum=80, bool useSSL=false)
 Connect to a WebSocket server using IP address with optional protocol negotiation.
 
static int Connect (IPADDR host, const char *resource, int portnum=80, bool useSSL=false)
 Connect to a WebSocket server using IP address without protocol negotiation.
 
static void DumpSockets ()
 Dump detailed state information for all active WebSocket connections.
 
static int GetFreeWebSocketCount ()
 Get the number of available WebSocket connection slots.
 

Public Attributes

int m_fd_tcp
 TCP socket file descriptor (underlying transport)
 
int m_fd_ws
 WebSocket file descriptor (application-level identifier)
 
OS_CRIT socket_crit
 Critical section for thread-safe socket operations.
 

Static Public Attributes

static int s_openSocketCount
 Number of currently open WebSocket connections.
 
static WebSocket WSSockStructs [WS_MAX_SOCKS]
 Static array of WebSocket connection structures.
 

Detailed Description

WebSocket connection management class.

Handles WebSocket protocol implementation including frame parsing, connection state, and bidirectional communication over TCP. Supports both client and server roles.

{

Member Function Documentation

◆ clroption()

int NB::WebSocket::clroption ( int option)

Clear a socket option on this WebSocket connection.

Parameters
optionThe socket option to clear (e.g., WS_SO_TEXT, TCP_NODELAY, SO_KEEPALIVE)
Returns
int Returns 0 on success, negative value on failure

This member function clears (disables) a specific socket option on the WebSocket connection. It is the counterpart to setoption() and allows you to disable previously enabled options or ensure certain options are not active.

Clearing an option returns the socket to its default behavior for that particular setting. This is useful when you need to:

  • Dynamically adjust socket behavior based on runtime conditions
  • Revert configuration changes
  • Switch between mutually exclusive options (e.g., text vs binary mode)
  • Restore default settings

Clearing an option that was never set has no effect and typically succeeds.

Note
Some options may not be clearable once set, depending on the underlying socket implementation. Check the return value to verify success.
See also
setoption() for setting socket options
getoption() for querying current option state
Example Usage

Expand for Example Usage

Examples

Disable Binary Mode
WebSocket *ws = GetActiveWebSocket();
// Clear binary mode, revert to default (text mode)
if (ws->clroption(WS_SO_BINARY) == 0) {
printf("Binary mode disabled, reverted to text mode\n");
} else {
printf("Failed to clear binary mode\n");
}
WebSocket connection management class.
Definition websockets.h:93
Disable TCP Keepalive
WebSocket *ws = GetActiveWebSocket();
// Disable keepalive (maybe to reduce network traffic)
if (ws->clroption(SO_KEEPALIVE) == 0) {
printf("TCP keepalive disabled\n");
} else {
printf("Failed to disable keepalive\n");
}
Re-enable Nagle's Algorithm
void RestoreBufferingMode(WebSocket *ws) {
// Clear TCP_NODELAY to re-enable Nagle's algorithm
if (ws->clroption(TCP_NODELAY) == 0) {
printf("Nagle's algorithm re-enabled (buffering mode)\n");
} else {
printf("WARNING: Could not re-enable buffering\n");
}
}
Toggle WebSocket Mode
void ToggleWebSocketMode(WebSocket *ws, bool enable_binary) {
if (enable_binary) {
ws->clroption(WS_SO_TEXT);
ws->setoption(WS_SO_BINARY);
printf("Switched to binary mode\n");
} else {
ws->clroption(WS_SO_BINARY);
ws->setoption(WS_SO_TEXT);
printf("Switched to text mode\n");
}
}
#define WS_SO_TEXT
Socket option flag to enable text frame mode (vs binary mode)
Definition websockets.h:72
Reset to Default Configuration
void ResetWebSocketOptions(WebSocket *ws) {
printf("Resetting WebSocket to default options...\n");
// Clear all custom options
ws->clroption(WS_SO_BINARY);
ws->clroption(WS_SO_COMPRESS);
ws->clroption(TCP_NODELAY);
ws->clroption(SO_KEEPALIVE);
// Set default text mode
ws->setoption(WS_SO_TEXT);
printf("WebSocket reset to defaults\n");
}
Conditional Option Clearing
void AdjustWebSocketForPowerMode(WebSocket *ws, bool low_power_mode) {
if (low_power_mode) {
// Disable features that increase power consumption
ws->clroption(SO_KEEPALIVE);
ws->clroption(TCP_NODELAY);
printf("Low power mode: disabled keepalive and nodelay\n");
} else {
// Re-enable performance features
ws->setoption(SO_KEEPALIVE);
ws->setoption(TCP_NODELAY);
printf("Normal power mode: enabled keepalive and nodelay\n");
}
}
Clear All Performance Options
void DisablePerformanceOptimizations(WebSocket *ws) {
printf("Disabling performance optimizations...\n");
int result = 0;
result |= ws->clroption(TCP_NODELAY);
result |= ws->clroption(WS_SO_COMPRESS);
if (result == 0) {
printf("All performance options cleared\n");
} else {
printf("WARNING: Some options failed to clear\n");
}
}
Cleanup Before Reconnection
void PrepareForReconnect(WebSocket *ws) {
// Clear all custom options before closing
ws->clroption(WS_SO_BINARY);
ws->clroption(TCP_NODELAY);
ws->clroption(SO_KEEPALIVE);
ws->clroption(WS_SO_COMPRESS);
printf("Options cleared, ready for reconnection\n");
}

◆ Connect() [1/4]

static int NB::WebSocket::Connect ( const char * host,
const char * resource,
const char * protocol,
int portnum = 80,
bool useSSL = false )
static

Connect to a WebSocket server using hostname with optional protocol negotiation.

Establishes a WebSocket client connection to the specified server by hostname, performing DNS resolution, TCP connection, HTTP upgrade handshake, and optional subprotocol negotiation. This is the primary method for creating client-side WebSocket connections.

Connection Process

  1. Resolve hostname to IP address via DNS
  2. Establish TCP connection to server:port
  3. Send HTTP upgrade request with WebSocket headers
  4. Negotiate subprotocol if specified
  5. Complete handshake and upgrade to WebSocket protocol
  6. Return WebSocket file descriptor on success

Parameters

Parameters
hostHostname or domain name of the WebSocket server (e.g., "ws.example.com"). Must be a null-terminated string. DNS resolution is performed automatically.
resourceWebSocket endpoint path (e.g., "/api/v1/stream", "/ws", "/chat"). Must start with "/" and be null-terminated. This is the path portion of the WebSocket URL.
protocolOptional subprotocol to negotiate (e.g., "chat", "mqtt", "json"). Pass NULL for no subprotocol negotiation. If specified, the server must support and accept this protocol during the handshake.
portnumTCP port number for the WebSocket connection (default: 80 for ws://, 443 for wss://). Standard ports are 80 (unencrypted) and 443 (SSL/TLS).
useSSLSet true to use WebSocket Secure (wss://) with TLS encryption, false for unencrypted WebSocket (ws://). Default is false.

Return Value

Returns a WebSocket file descriptor on success (positive integer), or a negative value on failure:

  • Positive value: Valid WebSocket file descriptor for use with WSRead(), WSWrite(), etc.
  • Negative value: Connection failed (DNS resolution, TCP connection, or handshake failure)

Connection Failures

Common failure scenarios:

  • DNS resolution failure (hostname not found)
  • TCP connection timeout or refused
  • HTTP upgrade rejected by server (non-101 response)
  • Protocol negotiation failure (requested protocol not supported)
  • No available WebSocket slots (WS_MAX_SOCKS limit reached)
  • Invalid handshake response from server

Usage Notes

  • The returned file descriptor must be closed with close() when done
  • Use WSRead() and WSWrite() for data exchange after successful connection
  • Check available slots with GetFreeWebSocketCount() before connecting
  • Connection is blocking; consider running in a separate task for non-blocking behavior
  • For wss:// (useSSL=true), ensure SSL/TLS libraries are linked and configured
  • Subprotocol is optional; many WebSocket servers don't require one
  • Default port 80 is typical for ws://, use 443 for wss://

Expand for Example Usage

Examples

Example 1: Basic WebSocket Client Connection
#include <websockets.h>
void WebSocketClientTask(void *pd) {
// Connect to WebSocket server
int wsFd = NB::WebSocket::Connect("echo.websocket.org",
"/",
NULL, // No protocol
80, // Standard port
false); // No SSL
if (wsFd < 0) {
printf("Failed to connect to WebSocket server\r\n");
return;
}
printf("Connected! WebSocket fd: %d\r\n", wsFd);
// Send a message
const char *msg = "Hello WebSocket!";
WSWrite(wsFd, msg, strlen(msg));
// Read response
char buffer[256];
int len = WSRead(wsFd, buffer, sizeof(buffer) - 1, TICKS_PER_SECOND * 5);
if (len > 0) {
buffer[len] = '\0';
printf("Received: %s\r\n", buffer);
}
// Clean up
close(wsFd);
printf("Connection closed\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.
#define TICKS_PER_SECOND
System clock ticks per second.
Definition constants.h:49
int close(int fd)
Close the specified file descriptor and free the associated resources.
Example 2: Secure WebSocket Connection (WSS)
void SecureWebSocketClient(void *pd) {
// Connect using WSS (WebSocket Secure)
int wsFd = NB::WebSocket::Connect("api.example.com",
"/v1/secure-stream",
"json-api", // Subprotocol
443, // HTTPS port
true); // Enable SSL/TLS
if (wsFd < 0) {
printf("Secure connection failed\r\n");
return;
}
printf("Secure WebSocket connected\r\n");
// Send JSON data
const char *jsonMsg = "{\"type\":\"subscribe\",\"channel\":\"updates\"}";
ws_setoption(wsFd, WS_SO_TEXT); // Text mode for JSON
WSWrite(wsFd, jsonMsg, strlen(jsonMsg));
// Receive updates
char rxBuffer[512];
while (1) {
int len = WSRead(wsFd, rxBuffer, sizeof(rxBuffer) - 1, TICKS_PER_SECOND * 30);
if (len > 0) {
rxBuffer[len] = '\0';
printf("Update: %s\r\n", rxBuffer);
} else {
printf("Connection closed or timeout\r\n");
break;
}
}
close(wsFd);
}
static int ws_setoption(int fd, int option)
Set a socket option on a WebSocket connection.
Example 3: Connection with Error Handling
int ConnectWithRetry(const char *host, const char *resource, int maxRetries) {
int wsFd = -1;
int retryCount = 0;
while (retryCount < maxRetries) {
// Check available slots
if (freeSlots <= 0) {
printf("No WebSocket slots available, waiting...\r\n");
OSTimeDly(TICKS_PER_SECOND * 5);
continue;
}
printf("Attempt %d/%d: Connecting to %s%s...\r\n",
retryCount + 1, maxRetries, host, resource);
wsFd = NB::WebSocket::Connect(host, resource, NULL, 80, false);
if (wsFd >= 0) {
printf("Successfully connected!\r\n");
return wsFd;
}
printf("Connection failed, retrying in 5 seconds...\r\n");
retryCount++;
OSTimeDly(TICKS_PER_SECOND * 5);
}
printf("Failed to connect after %d attempts\r\n", maxRetries);
return -1;
}
// Usage
int wsFd = ConnectWithRetry("ws.server.com", "/api/stream", 3);
if (wsFd >= 0) {
// Use WebSocket connection
close(wsFd);
}
static int GetFreeWebSocketCount()
Get the number of available WebSocket connection slots.
Example 4: Multiple Server Connections
struct ServerEndpoint {
const char *host;
const char *resource;
const char *protocol;
int port;
bool useSSL;
};
void ConnectMultipleServers(void *pd) {
ServerEndpoint servers[] = {
{"telemetry.example.com", "/data", "json", 80, false},
{"api.example.com", "/events", "mqtt", 443, true},
{"stream.example.com", "/live", NULL, 80, false}
};
int connections[3];
int connectedCount = 0;
for (int i = 0; i < 3; i++) {
printf("Connecting to %s%s...\r\n",
servers[i].host, servers[i].resource);
connections[i] = NB::WebSocket::Connect(servers[i].host,
servers[i].resource,
servers[i].protocol,
servers[i].port,
servers[i].useSSL);
if (connections[i] >= 0) {
printf(" Connected (fd %d)\r\n", connections[i]);
connectedCount++;
} else {
printf(" Connection failed\r\n");
}
}
printf("Successfully connected to %d/%d servers\r\n",
connectedCount, 3);
// Use connections...
// Clean up
for (int i = 0; i < 3; i++) {
if (connections[i] >= 0) {
close(connections[i]);
}
}
}
Example 5: Protocol Negotiation
void ConnectWithProtocol(void *pd) {
const char *protocols[] = {"chat", "json-rpc", "mqtt"};
int wsFd = -1;
// Try each protocol until one succeeds
for (int i = 0; i < 3; i++) {
printf("Attempting connection with protocol: %s\r\n", protocols[i]);
wsFd = NB::WebSocket::Connect("api.example.com",
"/ws",
protocols[i],
80,
false);
if (wsFd >= 0) {
printf("Connected with protocol: %s\r\n", protocols[i]);
break;
}
printf("Protocol %s not supported, trying next...\r\n", protocols[i]);
}
if (wsFd < 0) {
printf("No supported protocol found\r\n");
return;
}
// Use connection with negotiated protocol
// ...
close(wsFd);
}
Example 6: Non-blocking Connection in Task
void AsyncConnectTask(void *pd) {
const char *host = "stream.example.com";
const char *resource = "/live";
printf("Background connection task started\r\n");
// This blocks but runs in separate task
int wsFd = NB::WebSocket::Connect(host, resource, NULL, 80, false);
if (wsFd >= 0) {
printf("Background connection established\r\n");
// Store fd in global or notify main task
// ... handle communication ...
close(wsFd);
} else {
printf("Background connection failed\r\n");
}
}
void StartAsyncConnection() {
// Create task for non-blocking connection
OSSimpleTaskCreatewName(AsyncConnectTask,
MAIN_PRIO - 1,
NULL,
"WSConnect");
// Main thread continues without blocking
printf("Connection initiated in background\r\n");
}
#define MAIN_PRIO
Recommend UserMain priority.
Definition constants.h:130


See also
Connect(IPADDR, const char *, const char *, int, bool) - Connect using IP address
WSRead() - Read data from WebSocket connection
WSWrite() - Write data to WebSocket connection
close() - Close WebSocket connection
GetFreeWebSocketCount() - Check available connection slots
Note
DNS resolution may take several seconds; consider timeout handling
Connection blocks until complete or failure; use separate task for async behavior
Ensure WS_MAX_SOCKS has available slots before connecting
For production systems, implement connection retry logic with exponential backoff

◆ Connect() [2/4]

static int NB::WebSocket::Connect ( const char * host,
const char * resource,
int portnum = 80,
bool useSSL = false )
inlinestatic

Connect to a WebSocket server using hostname without protocol negotiation.

Convenience overload that establishes a WebSocket client connection without specifying a subprotocol. This is the most common connection method as many WebSocket servers don't require subprotocol negotiation.

Parameters

Parameters
hostHostname or domain name of the WebSocket server (e.g., "ws.example.com")
resourceWebSocket endpoint path (e.g., "/ws", "/api/stream")
portnumTCP port number (default: 80 for ws://, 443 for wss://)
useSSLSet true for wss:// (encrypted), false for ws:// (default: false)

Return Value

Returns WebSocket file descriptor (positive) on success, negative on failure.

See also
Connect(const char *, const char *, const char *, int, bool) - Full version with protocol
Note
This is equivalent to calling Connect(host, resource, NULL, portnum, useSSL)
Most commonly used overload for simple WebSocket connections

◆ Connect() [3/4]

static int NB::WebSocket::Connect ( IPADDR host,
const char * resource,
const char * protocol,
int portnum = 80,
bool useSSL = false )
static

Connect to a WebSocket server using IP address with optional protocol negotiation.

Establishes a WebSocket client connection using a direct IP address, bypassing DNS resolution. This is useful for local networks, static IPs, or when DNS is unavailable. Otherwise identical to the hostname-based Connect method.

Connection Process

  1. Use provided IP address directly (no DNS lookup)
  2. Establish TCP connection to IP:port
  3. Send HTTP upgrade request with Host header
  4. Negotiate subprotocol if specified
  5. Complete handshake and upgrade to WebSocket
  6. Return WebSocket file descriptor on success

Parameters

Parameters
hostIP address of the WebSocket server as IPADDR type. Can be constructed using AsciiToIp() for dotted notation or direct IP address types. Example: AsciiToIp("192.168.1.100") or IPADDR(192,168,1,100)
resourceWebSocket endpoint path (e.g., "/api/v1/stream", "/ws")
protocolOptional subprotocol to negotiate (NULL for none)
portnumTCP port number (default: 80 for ws://, 443 for wss://)
useSSLSet true for wss:// (encrypted), false for ws:// (default: false)

Return Value

Returns WebSocket file descriptor (positive) on success, negative on failure.

Advantages of IP Address Connection

  • Faster connection (no DNS resolution delay)
  • Works without DNS server availability
  • Deterministic connection to specific server
  • Useful for local network devices with static IPs
  • Avoids DNS caching issues during development

Expand for Example Usage

Examples

Example 1: Connect to Local WebSocket Server
void ConnectToLocalServer(void *pd) {
// Connect to WebSocket server at 192.168.1.100
IPADDR serverIP = AsciiToIp("192.168.1.100");
int wsFd = NB::WebSocket::Connect(serverIP,
"/api/data",
NULL, // No protocol
8080, // Custom port
false); // No SSL
if (wsFd < 0) {
printf("Connection to %s failed\r\n",
IpToString(serverIP));
return;
}
printf("Connected to server at %s:%d\r\n",
IpToString(serverIP), 8080);
// Use connection...
close(wsFd);
}
Used to hold and manipulate IPv4 and IPv6 addresses in dual stack mode.
Definition ipv6_addr.h:41
Example 2: Connect Using Direct IP Construction
void ConnectDirectIP(void *pd) {
// Construct IP address directly
IPADDR serverIP(192, 168, 1, 50);
int wsFd = NB::WebSocket::Connect(serverIP,
"/ws",
"telemetry",
80,
false);
if (wsFd >= 0) {
printf("Connected to 192.168.1.50\r\n");
// Send telemetry data
char data[128];
snprintf(data, sizeof(data),
"{\"temp\":%.1f,\"time\":%lu}", 25.5f, Secs);
WSWrite(wsFd, data, strlen(data));
close(wsFd);
}
}
Example 3: Fallback from Hostname to IP
int ConnectWithFallback(const char *hostname, const char *ipStr,
const char *resource) {
int wsFd;
// Try hostname first
printf("Attempting connection to %s...\r\n", hostname);
wsFd = NB::WebSocket::Connect(hostname, resource, NULL, 80, false);
if (wsFd >= 0) {
printf("Connected via hostname\r\n");
return wsFd;
}
// Fallback to IP address
printf("Hostname failed, trying IP address %s...\r\n", ipStr);
IPADDR serverIP = AsciiToIp(ipStr);
wsFd = NB::WebSocket::Connect(serverIP, resource, NULL, 80, false);
if (wsFd >= 0) {
printf("Connected via IP address\r\n");
return wsFd;
}
printf("All connection attempts failed\r\n");
return -1;
}
// Usage
int wsFd = ConnectWithFallback("server.local", "192.168.1.100", "/ws");
Example 4: Connect to Multiple Devices by IP
struct Device {
const char *name;
IPADDR ip;
int wsFd;
};
void ConnectToDeviceArray(Device *devices, int count) {
for (int i = 0; i < count; i++) {
printf("Connecting to %s at %s...\r\n",
devices[i].name, IpToString(devices[i].ip));
devices[i].wsFd = NB::WebSocket::Connect(devices[i].ip,
"/sensor",
"iot-proto",
80,
false);
if (devices[i].wsFd >= 0) {
printf(" %s: Connected (fd %d)\r\n",
devices[i].name, devices[i].wsFd);
} else {
printf(" %s: Connection failed\r\n", devices[i].name);
}
}
}
// Usage
Device sensors[] = {
{"Temperature", AsciiToIp("192.168.1.10"), -1},
{"Humidity", AsciiToIp("192.168.1.11"), -1},
{"Pressure", AsciiToIp("192.168.1.12"), -1}
};
ConnectToDeviceArray(sensors, 3);
Example 5: Dynamic IP Address Connection
void ConnectDynamicIP(void *pd) {
// Get IP address from configuration or discovery
char ipString[16];
GetServerIPFromConfig(ipString, sizeof(ipString));
IPADDR serverIP = AsciiToIp(ipString);
if (serverIP == IPADDR::NullIP()) {
printf("Invalid IP address: %s\r\n", ipString);
return;
}
printf("Connecting to dynamically configured server: %s\r\n",
IpToString(serverIP));
int wsFd = NB::WebSocket::Connect(serverIP, "/api", NULL, 80, false);
if (wsFd >= 0) {
printf("Connection successful\r\n");
// Use connection...
close(wsFd);
} else {
printf("Connection failed\r\n");
}
}
static IPADDR6 NullIP()
Static function to return a null IPADDR6 object.


See also
Connect(const char *, const char *, const char *, int, bool) - Connect using hostname
IPADDR - NetBurner IP address type
AsciiToIp() - Convert dotted notation string to IPADDR
IpToString() - Convert IPADDR to string for display
Note
No DNS lookup performed; connection is faster than hostname-based
Useful for local networks, embedded systems, and static configurations
Host header in HTTP upgrade still requires a hostname; IP is used for connection only

◆ Connect() [4/4]

static int NB::WebSocket::Connect ( IPADDR host,
const char * resource,
int portnum = 80,
bool useSSL = false )
inlinestatic

Connect to a WebSocket server using IP address without protocol negotiation.

Convenience overload that establishes a WebSocket client connection using an IP address without specifying a subprotocol. Most commonly used for local network connections or when the server doesn't require protocol negotiation.

Parameters

Parameters
hostIP address of the WebSocket server (IPADDR type)
resourceWebSocket endpoint path (e.g., "/ws", "/api/stream")
portnumTCP port number (default: 80 for ws://, 443 for wss://)
useSSLSet true for wss:// (encrypted), false for ws:// (default: false)

Return Value

Returns WebSocket file descriptor (positive) on success, negative on failure.

See also
Connect(IPADDR, const char *, const char *, int, bool) - Full version with protocol
Note
This is equivalent to calling Connect(host, resource, NULL, portnum, useSSL)
Most commonly used for local network WebSocket connections

◆ DumpSock()

void NB::WebSocket::DumpSock ( )

Dump detailed state information for this WebSocket connection.

Prints comprehensive diagnostic information about the current WebSocket connection to the debug console, including connection state, buffer levels, file descriptors, timing information, and internal state variables. Useful for debugging connection issues, buffer management, and protocol state.

Displayed Information

Typical output includes:

  • File descriptors (TCP and WebSocket)
  • Connection state (INIT, ESTABLISHED, CLOSING, etc.)
  • Buffer levels (RX, TX, Control)
  • Buffer statistics (used/available space)
  • Socket options (text/binary mode)
  • Timing information (last send, accumulation delay)
  • Frame processing state
  • Ping/pong state
  • Masking keys and payload lengths

Usage Scenarios

  • Debugging connection establishment issues
  • Diagnosing buffer exhaustion problems
  • Monitoring connection health and state
  • Troubleshooting frame fragmentation
  • Analyzing performance bottlenecks
  • Verifying protocol state transitions
  • Development and testing

Expand for Example Usage

Examples

Example 1: Basic Connection Diagnostics
void DiagnoseConnection(int wsFd) {
// Get WebSocket record
if (ws == NULL) {
printf("Invalid WebSocket fd: %d\r\n", wsFd);
return;
}
printf("=== WebSocket Diagnostics ===\r\n");
ws->DumpSock();
printf("=============================\r\n");
}
static WebSocket * GetWebSocketRecord(int fd)
Find a WebSocket record by its file descriptor.
Example 2: Debug Connection Issues
void DebugWebSocketCommunication(int wsFd) {
if (ws == NULL) return;
// Check state before operation
printf("Before write:\r\n");
ws->DumpSock();
// Attempt write
const char *msg = "Test message";
int result = WSWrite(wsFd, msg, strlen(msg));
// Check state after operation
printf("\nAfter write (result: %d):\r\n", result);
ws->DumpSock();
// Check buffer space
int space = ws->GetWriteSpace();
printf("\nAvailable TX buffer space: %d bytes\r\n", space);
}
Example 3: Monitor Connection Health
void MonitorConnectionHealth(int wsFd) {
if (ws == NULL) return;
while (1) {
// Periodic health check
OSTimeDly(TICKS_PER_SECOND * 10);
printf("\n=== Connection Health at %lu seconds ===\r\n", Secs);
ws->DumpSock();
// Check state
NB::WebSocket::WS_State state = ws->GetState();
if (state != NB::WebSocket::WS_ESTABLISHED) {
printf("WARNING: Connection not established!\r\n");
break;
}
// Check buffer levels
int space = ws->GetWriteSpace();
if (space < 100) {
printf("WARNING: Low TX buffer space (%d bytes)\r\n", space);
}
}
}
Example 4: Debugging Message Send Failure
void DebugSendFailure(int wsFd, const char *data, int len) {
if (ws == NULL) {
printf("Invalid WebSocket\r\n");
return;
}
printf("Attempting to send %d bytes...\r\n", len);
// Check pre-send state
int spaceBefore = ws->GetWriteSpace();
printf("TX buffer space before: %d bytes\r\n", spaceBefore);
if (spaceBefore < len) {
printf("ERROR: Insufficient buffer space!\r\n");
printf("Detailed state:\r\n");
ws->DumpSock();
return;
}
// Attempt send
int sent = WSWrite(wsFd, data, len);
if (sent < 0) {
printf("Send failed! Dumping state:\r\n");
ws->DumpSock();
} else if (sent < len) {
printf("Partial send: %d/%d bytes\r\n", sent, len);
ws->DumpSock();
} else {
printf("Send successful\r\n");
}
}
Example 5: Connection State Verification
void VerifyConnectionState(int wsFd, const char *operationName) {
if (ws == NULL) {
printf("%s: Invalid WebSocket fd\r\n", operationName);
return;
}
NB::WebSocket::WS_State state = ws->GetState();
printf("\n=== %s: State Verification ===\r\n", operationName);
ws->DumpSock();
switch (state) {
case NB::WebSocket::WS_ESTABLISHED:
printf("Status: OK - Connection ready\r\n");
break;
case NB::WebSocket::WS_CONNECTING:
printf("Status: WARNING - Still connecting\r\n");
break;
case NB::WebSocket::WS_CLOSE_PENDING:
case NB::WebSocket::WS_CLOSE_SENT:
case NB::WebSocket::WS_CLOSE_RECV:
printf("Status: WARNING - Connection closing\r\n");
break;
case NB::WebSocket::WS_CLOSED:
case NB::WebSocket::WS_FAIL:
printf("Status: ERROR - Connection closed/failed\r\n");
break;
default:
printf("Status: UNKNOWN - Unexpected state\r\n");
break;
}
}


See also
DumpSockets() - Dump state of all WebSocket connections
GetState() - Get current connection state
GetWriteSpace() - Get available TX buffer space
GetWebSocketRecord() - Get WebSocket object from file descriptor
Note
Output is sent to debug console (printf/serial)
Useful during development and testing
Can be called at any time during connection lifetime
For production code, consider removing or conditionally compiling debug calls
Not thread-safe; use appropriate synchronization if called from multiple tasks

◆ DumpSockets()

static void NB::WebSocket::DumpSockets ( )
static

Dump detailed state information for all active WebSocket connections.

Prints comprehensive diagnostic information for all WebSocket connections in the WSSockStructs array to the debug console. This includes both active and inactive slots, showing the overall state of the WebSocket subsystem. Useful for system-wide debugging and monitoring.

Displayed Information

For each WebSocket slot (0 to WS_MAX_SOCKS-1):

  • Slot index and active status
  • File descriptors (TCP and WebSocket)
  • Connection state
  • Buffer levels and statistics
  • Socket options and configuration
  • Timing and protocol state
  • Summary of active vs free connections

Usage Scenarios

  • System-wide connection monitoring
  • Debugging connection leaks or exhaustion
  • Verifying connection cleanup
  • Analyzing resource usage
  • Development and testing of multi-connection applications
  • Troubleshooting "no available slots" errors
  • System health checks

Expand for Example Usage

Examples

Example 1: System Health Check
void SystemHealthCheck(void *pd) {
while (1) {
// Periodic system-wide diagnostic
OSTimeDly(TICKS_PER_SECOND * 60);
printf("\n========================================\r\n");
printf("WebSocket System Status at %lu seconds\r\n", Secs);
printf("========================================\r\n");
printf("\nSummary: %d free slots, %d in use\r\n",
freeCount, WS_MAX_SOCKS - freeCount);
printf("========================================\r\n\n");
}
}
static void DumpSockets()
Dump detailed state information for all active WebSocket connections.
#define WS_MAX_SOCKS
Maximum number of simultaneous WebSocket connections supported.
Definition websockets.h:59
Example 2: Debug Connection Exhaustion
void DebugConnectionExhaustion() {
if (freeSlots <= 0) {
printf("\n!!! NO WEBSOCKET SLOTS AVAILABLE !!!\r\n");
printf("Dumping all connections to identify issue:\r\n\n");
printf("\nPossible causes:\r\n");
printf("- Connections not being closed properly\r\n");
printf("- Connection leaks in application code\r\n");
printf("- WS_MAX_SOCKS (%d) too low for application needs\r\n",
printf("- Connections stuck in non-terminal states\r\n");
} else {
printf("%d WebSocket slots available\r\n", freeSlots);
}
}
Example 3: Monitor Connection Lifecycle
void MonitorConnectionLifecycle() {
printf("Initial state:\r\n");
// Create some connections
int ws1 = NB::WebSocket::Connect("server1.com", "/ws", 80);
printf("\nAfter connecting to server1:\r\n");
int ws2 = NB::WebSocket::Connect("server2.com", "/api", 80);
printf("\nAfter connecting to server2:\r\n");
// Use connections...
OSTimeDly(TICKS_PER_SECOND * 5);
// Close first connection
close(ws1);
printf("\nAfter closing server1 connection:\r\n");
// Close second connection
close(ws2);
printf("\nAfter closing server2 connection:\r\n");
}
Example 4: Find Stuck Connections
void FindStuckConnections() {
printf("Searching for stuck connections...\r\n\n");
// Analyze the output
printf("\nAnalyzing connection states:\r\n");
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->m_fd_ws >= 0) { // Active connection
NB::WebSocket::WS_State state = ws->GetState();
if (state == NB::WebSocket::WS_CLOSE_PENDING ||
state == NB::WebSocket::WS_CLOSE_SENT) {
printf(" Slot %d: Stuck in closing state\r\n", i);
} else if (state == NB::WebSocket::WS_CONNECTING) {
printf(" Slot %d: Stuck in connecting state\r\n", i);
}
}
}
}
static WebSocket WSSockStructs[WS_MAX_SOCKS]
Static array of WebSocket connection structures.
Definition websockets.h:4191
Example 5: Application Startup Verification
void VerifyWebSocketSubsystem() {
printf("Verifying WebSocket subsystem initialization...\r\n");
// Check initial state
if (freeCount != WS_MAX_SOCKS) {
printf("WARNING: Expected %d free slots, found %d\r\n",
WS_MAX_SOCKS, freeCount);
printf("Dumping connections:\r\n");
} else {
printf("OK: All %d WebSocket slots available\r\n", WS_MAX_SOCKS);
}
}
Example 6: Debug Handler for Console Command
// Console command handler for runtime diagnostics
void HandleWSDumpCommand(void) {
printf("\n");
printf("========================================\r\n");
printf(" WebSocket Diagnostic Dump\r\n");
printf(" Time: %lu seconds\r\n", Secs);
printf("========================================\r\n\n");
// Show system stats
int activeCount = WS_MAX_SOCKS - freeCount;
printf("Active connections: %d/%d\r\n", activeCount, WS_MAX_SOCKS);
printf("Free slots: %d\r\n\n", freeCount);
// Dump all connections
printf("\n========================================\r\n");
}
// Register in UserMain
void UserMain(void *pd) {
init();
// ... other initialization ...
// Register debug command
RegisterConsoleCommand("wsdump", HandleWSDumpCommand);
while (1) {
OSTimeDly(TICKS_PER_SECOND);
}
}
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...


See also
DumpSock() - Dump state of single WebSocket connection
GetFreeWebSocketCount() - Get number of available connection slots
WSSockStructs - Static array of all WebSocket structures
WS_MAX_SOCKS - Maximum number of WebSocket connections
Note
Output is sent to debug console (printf/serial)
Can generate significant output (all WS_MAX_SOCKS slots)
Useful during development, testing, and troubleshooting
Consider removing or conditionally compiling for production
Not thread-safe; use appropriate synchronization if needed
Inactive slots are also displayed for complete system view

◆ Flush()

void NB::WebSocket::Flush ( )

Flush buffered transmit data to the network.

This member function forces any pending data in the WebSocket's transmit buffer to be sent immediately to the peer over the network. Normally, the WebSocket implementation may buffer small amounts of data to optimize network usage through packet coalescing, but this function overrides that behavior to ensure immediate transmission.

The flush operation is essential for:

  • Ensuring critical messages are sent without delay
  • Completing data transmission before closing a connection
  • Real-time applications requiring immediate delivery
  • Interactive protocols with strict timing requirements
  • Preventing buffer buildup in high-throughput scenarios
  • Coordinating with time-sensitive operations

The function blocks until the buffer contents have been passed to the underlying TCP/IP stack, though actual network transmission depends on network conditions and the peer's receive capability.

Note
This function blocks until the flush operation completes or a timeout occurs. The duration depends on buffer size and network speed.
Flushing does not guarantee the peer has received the data, only that it has been transmitted from the local buffer.
Warning
Excessive flushing can reduce network efficiency by preventing optimal packet coalescing. Use only when immediate transmission is required.
See also
GetWriteSpace() to check buffer space before writing
write() for sending data to the WebSocket
Example Usage

Expand for Example Usage

Examples

Basic Flush After Write
WebSocket *ws = GetActiveWebSocket();
// Send data
const char *message = "Critical alert message";
ws->write(message, strlen(message));
// Ensure it's sent immediately
ws->Flush();
printf("Message flushed to network\n");
Flush Before Closing Connection
void CloseWebSocketGracefully(WebSocket *ws) {
// Send final goodbye message
ws->write("Connection closing", 18);
// Ensure message is sent before closing
ws->Flush();
// Brief delay to allow network transmission
OSTimeDly(TICKS_PER_SECOND / 10);
// Now safe to close
ws->close();
printf("Connection closed gracefully\n");
}
Real-Time Data Streaming
void StreamSensorData(WebSocket *ws) {
while (IsStreamingActive()) {
// Read sensor values
float temperature = ReadTemperatureSensor();
float pressure = ReadPressureSensor();
// Format data
char buffer[128];
snprintf(buffer, sizeof(buffer),
"{\"temp\":%.2f,\"pressure\":%.2f}",
temperature, pressure);
// Send and flush for real-time delivery
ws->write(buffer, strlen(buffer));
ws->Flush();
// Wait for next sample
OSTimeDly(TICKS_PER_SECOND);
}
}
Command-Response Pattern
void HandleCommand(WebSocket *ws, const char *command) {
char response[256];
// Process command
ProcessCommand(command, response, sizeof(response));
// Send response
ws->write(response, strlen(response));
// Flush to ensure prompt delivery to client
ws->Flush();
printf("Command response sent and flushed\n");
}
Batch Transmission with Final Flush
void SendBatchMessages(WebSocket *ws, const char **messages, int count) {
printf("Sending batch of %d messages...\n", count);
// Send all messages
for (int i = 0; i < count; i++) {
ws->write(messages[i], strlen(messages[i]));
}
// Single flush at the end for efficiency
ws->Flush();
printf("Batch sent and flushed\n");
}
Interactive Chat Application
void SendChatMessage(WebSocket *ws, const char *user, const char *message) {
char formatted[512];
// Format chat message with timestamp
snprintf(formatted, sizeof(formatted),
"{\"time\":%lu,\"user\":\"%s\",\"msg\":\"%s\"}",
Secs, user, message);
// Send immediately for interactive feel
ws->write(formatted, strlen(formatted));
ws->Flush();
}
Emergency Alert System
void SendEmergencyAlert(WebSocket *ws, const char *alert_type,
const char *details) {
char alert[256];
snprintf(alert, sizeof(alert),
"{\"type\":\"EMERGENCY\",\"alert\":\"%s\",\"details\":\"%s\"}",
alert_type, details);
// Critical message - send immediately
ws->write(alert, strlen(alert));
ws->Flush();
printf("EMERGENCY alert sent: %s\n", alert_type);
}
Periodic Status Updates
void StatusUpdateTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
while (1) {
if (ws->GetState() == WS_STATE_OPEN) {
// Gather system status
char status[256];
GetSystemStatus(status, sizeof(status));
// Send status update
ws->write(status, strlen(status));
ws->Flush();
printf("Status update sent\n");
}
// Update every 10 seconds
OSTimeDly(TICKS_PER_SECOND * 10);
}
}
Flow Control with Flush
bool SendLargeDataWithFlush(WebSocket *ws, const char *data, int length) {
const int CHUNK_SIZE = 1024;
int bytes_sent = 0;
while (bytes_sent < length) {
// Check available buffer space
int space = ws->GetWriteSpace();
if (space < 256) {
// Low on space, flush to make room
printf("Flushing buffer (space: %d bytes)\n", space);
ws->Flush();
OSTimeDly(TICKS_PER_SECOND / 10);
}
// Send next chunk
int to_send = (length - bytes_sent);
if (to_send > CHUNK_SIZE) {
to_send = CHUNK_SIZE;
}
ws->write(data + bytes_sent, to_send);
bytes_sent += to_send;
}
// Final flush
ws->Flush();
printf("Large data transfer complete: %d bytes\n", bytes_sent);
return true;
}
Conditional Flush Based on Priority
void SendMessage(WebSocket *ws, const char *msg, bool high_priority) {
ws->write(msg, strlen(msg));
if (high_priority) {
// Flush immediately for high priority messages
ws->Flush();
printf("High priority message flushed\n");
} else {
// Let normal priority messages be buffered
printf("Normal priority message buffered\n");
}
}

◆ GetFlushTime()

static void NB::WebSocket::GetFlushTime ( )
static

Updates the global flush timing for WebSocket message delivery.

Description

This static function updates timing-related variables that control when buffered WebSocket messages should be flushed to the network. It helps optimize message batching and delivery by determining appropriate flush intervals based on system conditions, message priorities, or application requirements.

Usage Notes

  • Typically called periodically or when flush timing needs recalculation
  • May read system time (Secs, TimeTick) to update timing thresholds
  • Used to implement Nagle-like algorithms for message coalescing
  • Should be lightweight as it may be called frequently
  • May update static/global timing variables used by flush logic

Flush Timing Strategies

Common timing strategies include:

  • Fixed interval flushing (e.g., every 100ms)
  • Adaptive timing based on message rate
  • Priority-based immediate vs. delayed flush
  • Buffer-size threshold triggers

Thread Safety

If timing variables are accessed from multiple contexts, appropriate synchronization must be used.

See Also

  • ws_write_notify() - Uses flush timing to control message delivery
  • Message buffering and coalescing functions
  • Secs, TimeTick - NetBurner timing variables

Expand for Example Usage

Examples

Example 1: Fixed Interval Flush Timing
class WebSocketManager {
private:
static uint32_t nextFlushTime;
static const uint32_t FLUSH_INTERVAL_MS = 100;
static void GetFlushTime() {
// Update next flush time to current time + interval
nextFlushTime = TimeTick + (FLUSH_INTERVAL_MS * TICKS_PER_SECOND / 1000);
}
public:
static bool ShouldFlush() {
return (TimeTick >= nextFlushTime);
}
static void Initialize() {
GetFlushTime(); // Set initial flush time
}
};
uint32_t WebSocketManager::nextFlushTime = 0;
Example 2: Adaptive Flush Timing Based on Load
static uint32_t flushIntervalTicks = TICKS_PER_SECOND / 10; // Default 100ms
static uint32_t lastFlushTime = 0;
static uint32_t messagesSinceFlush = 0;
static void GetFlushTime() {
uint32_t currentTime = TimeTick;
// Calculate time since last flush
uint32_t elapsedTicks = currentTime - lastFlushTime;
// Adapt interval based on message rate
if (messagesSinceFlush > 100 && elapsedTicks < flushIntervalTicks) {
// High message rate - flush more frequently
flushIntervalTicks = flushIntervalTicks / 2;
if (flushIntervalTicks < TICKS_PER_SECOND / 100) {
flushIntervalTicks = TICKS_PER_SECOND / 100; // Min 10ms
}
}
else if (messagesSinceFlush < 10 && elapsedTicks > flushIntervalTicks) {
// Low message rate - flush less frequently
flushIntervalTicks = flushIntervalTicks * 2;
if (flushIntervalTicks > TICKS_PER_SECOND) {
flushIntervalTicks = TICKS_PER_SECOND; // Max 1 second
}
}
lastFlushTime = currentTime;
messagesSinceFlush = 0;
}
static void GetFlushTime()
Updates the global flush timing for WebSocket message delivery.
Example 3: Priority-Based Flush Timing
struct FlushTiming {
uint32_t normalFlushTime;
uint32_t priorityFlushTime;
uint32_t idleFlushTime;
bool hasPriorityMessages;
};
static FlushTiming globalFlushTiming;
static void GetFlushTime() {
uint32_t currentTime = TimeTick;
// Update timing based on message priorities in queue
if (globalFlushTiming.hasPriorityMessages) {
// Priority messages - flush immediately
globalFlushTiming.priorityFlushTime = currentTime;
printf("Priority flush scheduled immediately\r\n");
}
else if (GetQueuedMessageCount() > 50) {
// Many messages - flush soon (50ms)
globalFlushTiming.normalFlushTime = currentTime + (TICKS_PER_SECOND / 20);
}
else if (GetQueuedMessageCount() > 0) {
// Few messages - batch for efficiency (200ms)
globalFlushTiming.normalFlushTime = currentTime + (TICKS_PER_SECOND / 5);
}
else {
// Idle - flush any stale data (1 second)
globalFlushTiming.idleFlushTime = currentTime + TICKS_PER_SECOND;
}
}
Example 4: Integration with Periodic Update Task
void WebSocketUpdateTask(void* pd) {
while (1) {
// Update flush timing every 50ms
OSTimeDly(TICKS_PER_SECOND / 20);
// Check if flush is needed
if (ShouldFlushNow()) {
FlushAllConnections();
}
}
}
void StartWebSocketManager() {
// Initialize timing
// Start update task
OSTaskCreate(WebSocketUpdateTask,
nullptr,
&TaskStack[USER_TASK_STK_SIZE],
TaskStack,
WS_UPDATE_TASK_PRIO);
}

◆ GetFreeWebSocketCount()

static int NB::WebSocket::GetFreeWebSocketCount ( )
static

Get the number of available WebSocket connection slots.

Returns the count of free (unused) WebSocket connection slots available for new connections. This is useful for checking resource availability before attempting to create new connections, preventing "no slots available" errors.

Return Value

Returns the number of free connection slots (0 to WS_MAX_SOCKS):

  • WS_MAX_SOCKS: All slots free, no active connections
  • 1 to WS_MAX_SOCKS-1: Some slots free, some in use
  • 0: All slots in use, cannot accept new connections

Usage Guidelines

  • Check before calling Connect() or WSUpgrade() to avoid failures
  • Monitor resource usage in multi-connection applications
  • Implement connection limits and queuing strategies
  • Use for load shedding and resource management
  • Display in status interfaces and diagnostics

Expand for Example Usage

Examples

Example 1: Check Before Connecting
void SafeConnect(const char *host, const char *resource) {
// Check if slots available
if (freeSlots <= 0) {
printf("Cannot connect: No WebSocket slots available\r\n");
printf("Active connections: %d/%d\r\n",
return;
}
printf("Connecting (%d slots available)...\r\n", freeSlots);
int wsFd = NB::WebSocket::Connect(host, resource, NULL, 80, false);
if (wsFd >= 0) {
printf("Connected successfully (fd %d)\r\n", wsFd);
printf("Remaining slots: %d\r\n",
} else {
printf("Connection failed\r\n");
}
}
Example 2: Server Connection Limit
int HandleWebSocketUpgrade(HTTP_Request *req, int sock) {
// Reserve one slot for admin/priority connections
const int RESERVED_SLOTS = 1;
if (freeSlots <= RESERVED_SLOTS) {
printf("Rejecting connection: Only %d slots free (need %d reserved)\r\n",
freeSlots, RESERVED_SLOTS);
writestring(sock, "HTTP/1.1 503 Service Unavailable\r\n");
writestring(sock, "Content-Type: text/plain\r\n\r\n");
writestring(sock, "Server at capacity");
return -1;
}
printf("Accepting WebSocket (%d slots remaining)\r\n",
freeSlots - 1);
if (WSUpgrade(req, sock) >= 0) {
OSSimpleTaskCreatewName(WebSocketTask, MAIN_PRIO - 1,
(void *)(intptr_t)sock, "WSTask");
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)....
int WSUpgrade(HTTP_Request *req, int sock)
Upgrade an HTTP connection to a WebSocket connection.
HTTP Request Structure.
Definition http.h:87
Example 3: Connection Queue Management
#define MAX_QUEUED_CONNECTIONS 10
struct QueuedConnection {
char host[64];
char resource[64];
bool pending;
};
QueuedConnection connQueue[MAX_QUEUED_CONNECTIONS];
int queueCount = 0;
OS_CRIT queueCrit;
void QueueConnection(const char *host, const char *resource) {
OSCritEnter(&queueCrit, TICKS_PER_SECOND);
if (queueCount >= MAX_QUEUED_CONNECTIONS) {
printf("Connection queue full\r\n");
OSCritLeave(&queueCrit);
return;
}
strncpy(connQueue[queueCount].host, host, 63);
strncpy(connQueue[queueCount].resource, resource, 63);
connQueue[queueCount].pending = true;
queueCount++;
OSCritLeave(&queueCrit);
printf("Connection queued (%d in queue)\r\n", queueCount);
}
void ProcessConnectionQueue(void *pd) {
while (1) {
OSTimeDly(TICKS_PER_SECOND);
// Check if we can process queued connections
if (freeSlots > 0 && queueCount > 0) {
OSCritEnter(&queueCrit, TICKS_PER_SECOND);
printf("Processing queue (%d pending, %d slots)\r\n",
queueCount, freeSlots);
for (int i = 0; i < queueCount && freeSlots > 0; i++) {
if (connQueue[i].pending) {
int wsFd = NB::WebSocket::Connect(connQueue[i].host,
connQueue[i].resource,
NULL, 80, false);
if (wsFd >= 0) {
printf("Queued connection established\r\n");
connQueue[i].pending = false;
freeSlots--;
}
}
}
// Compact queue
int newCount = 0;
for (int i = 0; i < queueCount; i++) {
if (connQueue[i].pending) {
if (i != newCount) {
connQueue[newCount] = connQueue[i];
}
newCount++;
}
}
queueCount = newCount;
OSCritLeave(&queueCrit);
}
}
}
Example 4: Resource Usage Display
void DisplayWebSocketStatus() {
int usedSlots = WS_MAX_SOCKS - freeSlots;
int percentUsed = (usedSlots * 100) / WS_MAX_SOCKS;
printf("WebSocket Resource Usage:\r\n");
printf(" Active: %d/%d (%d%%)\r\n",
usedSlots, WS_MAX_SOCKS, percentUsed);
printf(" Free: %d\r\n", freeSlots);
// Visual bar
printf(" [");
for (int i = 0; i < WS_MAX_SOCKS; i++) {
printf(i < usedSlots ? "#" : "-");
}
printf("]\r\n");
// Warning for high usage
if (percentUsed >= 75) {
printf(" WARNING: High resource usage!\r\n");
}
}
Example 5: Adaptive Connection Strategy
typedef enum {
STRATEGY_AGGRESSIVE, // Use all available slots
STRATEGY_CONSERVATIVE,// Keep some slots reserved
STRATEGY_MINIMAL // Use minimal connections
} ConnectionStrategy;
int GetMaxAllowedConnections(ConnectionStrategy strategy) {
switch (strategy) {
case STRATEGY_AGGRESSIVE:
// Use all but one slot
return (freeSlots > 1) ? (WS_MAX_SOCKS - 1) : 0;
case STRATEGY_CONSERVATIVE:
// Use up to 75% of total
return (WS_MAX_SOCKS * 3) / 4;
case STRATEGY_MINIMAL:
// Use up to 50% of total
return WS_MAX_SOCKS / 2;
default:
return 0;
}
}
bool CanCreateConnection(ConnectionStrategy strategy) {
int maxAllowed = GetMaxAllowedConnections(strategy);
return currentUsed < maxAllowed;
}
Example 6: Periodic Resource Monitoring
void MonitorWebSocketResources(void *pd) {
int lastFree = -1;
while (1) {
OSTimeDly(TICKS_PER_SECOND * 5);
// Report changes
if (freeNow != lastFree) {
printf("WebSocket slots changed: %d -> %d free\r\n",
lastFree, freeNow);
if (freeNow == 0) {
printf("ALERT: All WebSocket slots in use!\r\n");
} else if (freeNow == WS_MAX_SOCKS) {
printf("INFO: All WebSocket slots now free\r\n");
}
lastFree = freeNow;
}
}
}


See also
WS_MAX_SOCKS - Maximum number of WebSocket connections
Connect() - Create new WebSocket client connection
WSUpgrade() - Upgrade HTTP connection to WebSocket (server-side)
DumpSockets() - Display state of all connections
s_openSocketCount - Static member tracking open connection count
Note
Return value indicates available slots, not necessarily successful connection
Value can change between check and connection attempt (race condition in multi-threaded)
Always handle connection failure even if slots appear available
Thread-safe; uses internal synchronization

◆ getoption()

int NB::WebSocket::getoption ( )

Get the current socket options bitmap.

Returns
int A bitmask of currently enabled socket options, or negative value on failure

This member function retrieves the current state of all socket options for this WebSocket connection. It returns a bitmask where each bit represents a specific socket option's state (enabled/disabled).

The returned value can be tested against specific option flags using bitwise AND operations to determine if particular options are currently enabled.

Common uses include:

  • Debugging socket configuration
  • Verifying option settings after configuration
  • Conditionally changing options based on current state
  • Logging and diagnostics
  • Saving/restoring socket configuration

Example option flags that may be present in the bitmask:

  • WS_SO_TEXT: Text frame mode enabled
  • WS_SO_BINARY: Binary frame mode enabled
  • TCP_NODELAY: Nagle's algorithm disabled
  • SO_KEEPALIVE: TCP keepalive enabled
  • WS_SO_COMPRESS: Compression enabled
Note
The exact interpretation of the bitmask depends on the WebSocket implementation and which options are supported.
The returned bitmask is a snapshot of the current state. It may change as options are set or cleared.
See also
setoption() for setting socket options
clroption() for clearing socket options
Example Usage

Expand for Example Usage

Examples

Query Current Options
WebSocket *ws = GetActiveWebSocket();
int options = ws->getoption();
if (options >= 0) {
printf("Current socket options: 0x%08X\n", options);
} else {
printf("Failed to get socket options\n");
}
Check Specific Option
WebSocket *ws = GetActiveWebSocket();
int options = ws->getoption();
if (options >= 0) {
if (options & WS_SO_BINARY) {
printf("WebSocket is in BINARY mode\n");
} else if (options & WS_SO_TEXT) {
printf("WebSocket is in TEXT mode\n");
} else {
printf("WebSocket mode unknown\n");
}
if (options & TCP_NODELAY) {
printf("Low latency mode: Nagle's algorithm DISABLED\n");
} else {
printf("Buffering mode: Nagle's algorithm ENABLED\n");
}
if (options & SO_KEEPALIVE) {
printf("TCP keepalive: ENABLED\n");
} else {
printf("TCP keepalive: DISABLED\n");
}
}
Conditional Option Setting
void EnsureBinaryModeEnabled(WebSocket *ws) {
int options = ws->getoption();
if (options < 0) {
printf("ERROR: Cannot read socket options\n");
return;
}
// Only set binary mode if not already enabled
if (!(options & WS_SO_BINARY)) {
ws->setoption(WS_SO_BINARY);
printf("Binary mode was disabled, now enabled\n");
} else {
printf("Binary mode already enabled\n");
}
}
Diagnostic Logging
void LogWebSocketConfiguration(WebSocket *ws) {
int options = ws->getoption();
if (options < 0) {
printf("ERROR: Could not retrieve socket options\n");
return;
}
printf("=== WebSocket Configuration ===\n");
printf("Frame Mode:\n");
printf(" WS_SO_TEXT: %s\n", (options & WS_SO_TEXT) ? "ON" : "OFF");
printf(" WS_SO_BINARY: %s\n", (options & WS_SO_BINARY) ? "ON" : "OFF");
printf("TCP Options:\n");
printf(" SO_KEEPALIVE: %s\n", (options & SO_KEEPALIVE) ? "ON" : "OFF");
printf(" TCP_NODELAY: %s\n", (options & TCP_NODELAY) ? "ON" : "OFF");
printf("Features:\n");
printf(" WS_SO_COMPRESS:%s\n", (options & WS_SO_COMPRESS) ? "ON" : "OFF");
printf("Raw bitmask: 0x%08X\n", options);
printf("===============================\n");
}
Compare Configurations
void CompareWebSocketOptions(WebSocket *ws1, WebSocket *ws2) {
int options1 = ws1->getoption();
int options2 = ws2->getoption();
if (options1 < 0 || options2 < 0) {
printf("ERROR: Could not retrieve options for comparison\n");
return;
}
if (options1 == options2) {
printf("Both WebSockets have identical options (0x%08X)\n",
options1);
} else {
printf("WebSocket options differ:\n");
printf(" WS1: 0x%08X\n", options1);
printf(" WS2: 0x%08X\n", options2);
int diff = options1 ^ options2;
printf(" Differences: 0x%08X\n", diff);
// Show which specific options differ
if (diff & WS_SO_BINARY) {
printf(" - Binary mode differs\n");
}
if (diff & TCP_NODELAY) {
printf(" - TCP_NODELAY differs\n");
}
if (diff & SO_KEEPALIVE) {
printf(" - Keepalive differs\n");
}
}
}
int getoption()
Get the current socket options bitmap.
Save and Restore Configuration
int SaveWebSocketConfig(WebSocket *ws) {
int saved_options = ws->getoption();
if (saved_options >= 0) {
printf("Configuration saved: 0x%08X\n", saved_options);
}
return saved_options;
}
void RestoreWebSocketConfig(WebSocket *ws, int saved_options) {
if (saved_options < 0) {
printf("Invalid saved configuration\n");
return;
}
// Clear all options first
ws->clroption(WS_SO_BINARY);
ws->clroption(WS_SO_TEXT);
ws->clroption(TCP_NODELAY);
ws->clroption(SO_KEEPALIVE);
ws->clroption(WS_SO_COMPRESS);
// Restore saved options
if (saved_options & WS_SO_BINARY) ws->setoption(WS_SO_BINARY);
if (saved_options & WS_SO_TEXT) ws->setoption(WS_SO_TEXT);
if (saved_options & TCP_NODELAY) ws->setoption(TCP_NODELAY);
if (saved_options & SO_KEEPALIVE) ws->setoption(SO_KEEPALIVE);
if (saved_options & WS_SO_COMPRESS) ws->setoption(WS_SO_COMPRESS);
printf("Configuration restored: 0x%08X\n", saved_options);
}
// Usage:
int saved = SaveWebSocketConfig(ws);
// ... modify options ...
RestoreWebSocketConfig(ws, saved);
Verify Configuration After Setup
bool VerifyWebSocketSetup(WebSocket *ws, int expected_options) {
int actual_options = ws->getoption();
if (actual_options < 0) {
printf("ERROR: Cannot verify configuration\n");
return false;
}
if ((actual_options & expected_options) == expected_options) {
printf("Configuration verified successfully\n");
return true;
} else {
printf("Configuration mismatch!\n");
printf(" Expected: 0x%08X\n", expected_options);
printf(" Actual: 0x%08X\n", actual_options);
printf(" Missing: 0x%08X\n",
expected_options & ~actual_options);
return false;
}
}
// Usage:
ws->setoption(WS_SO_BINARY | TCP_NODELAY);
VerifyWebSocketSetup(ws, WS_SO_BINARY | TCP_NODELAY);
Runtime Option Monitoring
void MonitorWebSocketOptions(WebSocket *ws) {
static int last_options = -1;
int current_options = ws->getoption();
if (current_options >= 0) {
if (current_options != last_options) {
printf("Options changed: 0x%08X -> 0x%08X\n",
last_options, current_options);
LogWebSocketConfiguration(ws);
last_options = current_options;
}
}
}

◆ GetPingReplyTick()

int NB::WebSocket::GetPingReplyTick ( uint32_t * replyTick)

Get the tick count when the last pong was received.

Parameters
replyTickPointer to variable to receive the tick count of last pong reception
Returns
int Returns 0 on success with valid tick count, negative value on error

This function retrieves the system tick count (from TimeTick) at the moment the most recent pong frame was received. This timestamp can be used in conjunction with the ping send timestamp from Ping() to calculate precise round-trip time.

The function is useful for:

  • Calculating round-trip time (RTT) measurements
  • Verifying pong reception without blocking
  • Implementing non-blocking ping-pong monitoring
  • Logging network performance metrics
  • Detecting when the last successful communication occurred

The returned tick count represents the absolute system time when the pong was received. Subtract the ping send tick (from Ping()) to get elapsed ticks, then convert to milliseconds using TICKS_PER_SECOND:

RTT_ms = ((replyTick - sentTick) * 1000) / TICKS_PER_SECOND

Note
This function returns the timestamp of the LAST received pong, which may correspond to an earlier ping if multiple pings have been sent.
If no pong has been received since connection establishment, the function may return an error or an initial/invalid timestamp.
This is a non-blocking call that returns immediately with the stored timestamp value.
See also
Ping() to send ping frames and get send timestamps
WaitForPingReply() for blocking wait on pong reception
Example Usage

Expand for Example Usage

Examples

Calculate Round-Trip Time
WebSocket *ws = GetActiveWebSocket();
uint32_t sent_tick;
// Send ping
if (ws->Ping(0, &sent_tick) == 0) {
// Wait for pong
if (ws->WaitForPingReply(TICKS_PER_SECOND * 3) == 0) {
uint32_t reply_tick;
// Get pong timestamp
if (ws->GetPingReplyTick(&reply_tick) == 0) {
// Calculate RTT
uint32_t rtt_ticks = reply_tick - sent_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
printf("Round-trip time: %.3f ms\n", rtt_ms);
}
}
}
Non-Blocking Pong Check
bool CheckForPongReceived(WebSocket *ws, uint32_t ping_sent_tick) {
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) == 0) {
// Check if this pong is newer than our ping
if (reply_tick > ping_sent_tick) {
uint32_t rtt_ticks = reply_tick - ping_sent_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
printf("Pong received: RTT = %.2f ms\n", rtt_ms);
return true;
}
}
return false;
}
Track Last Communication Time
uint32_t GetTimeSinceLastPong(WebSocket *ws) {
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) != 0) {
printf("No pong received yet\n");
return 0xFFFFFFFF; // Max value indicates no pong
}
uint32_t current_tick = TimeTick;
uint32_t elapsed_ticks = current_tick - reply_tick;
// Convert to seconds
uint32_t elapsed_seconds = elapsed_ticks / TICKS_PER_SECOND;
return elapsed_seconds;
}
// Usage:
uint32_t idle_time = GetTimeSinceLastPong(ws);
if (idle_time > 60) {
printf("WARNING: No pong received for %lu seconds\n", idle_time);
}
Periodic RTT Monitoring
void MonitorRTT(WebSocket *ws) {
static uint32_t last_ping_tick = 0;
static float rtt_history[10];
static int rtt_index = 0;
uint32_t current_tick = TimeTick;
// Send ping every 5 seconds
if ((current_tick - last_ping_tick) > (TICKS_PER_SECOND * 5)) {
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) == 0) {
last_ping_tick = sent_tick;
}
}
// Check for pong
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) == 0) {
if (reply_tick > last_ping_tick) {
// Calculate and store RTT
uint32_t rtt_ticks = reply_tick - last_ping_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
rtt_history[rtt_index] = rtt_ms;
rtt_index = (rtt_index + 1) % 10;
// Calculate average
float avg_rtt = 0;
for (int i = 0; i < 10; i++) {
avg_rtt += rtt_history[i];
}
avg_rtt /= 10.0f;
printf("RTT: %.2f ms (avg: %.2f ms)\n", rtt_ms, avg_rtt);
}
}
}
Detect Slow Responses
void DetectSlowConnection(WebSocket *ws, uint32_t ping_sent_tick,
float threshold_ms) {
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) == 0) {
if (reply_tick > ping_sent_tick) {
uint32_t rtt_ticks = reply_tick - ping_sent_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
if (rtt_ms > threshold_ms) {
printf("WARNING: Slow connection detected: %.2f ms "
"(threshold: %.2f ms)\n", rtt_ms, threshold_ms);
} else {
printf("Connection speed acceptable: %.2f ms\n", rtt_ms);
}
}
}
}
Log Pong Reception
void LogPongReception(WebSocket *ws, const char *label) {
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) == 0) {
uint32_t current_time = Secs;
printf("[%lu] %s: Pong received at tick %lu\n",
current_time, label, reply_tick);
// Log to persistent storage
LogToFile(current_time, label, reply_tick);
} else {
printf("%s: No pong data available\n", label);
}
}
Compare Multiple Ping Attempts
void ComparePingAttempts(WebSocket *ws) {
const int ATTEMPTS = 5;
uint32_t ping_ticks[ATTEMPTS];
float rtts[ATTEMPTS];
for (int i = 0; i < ATTEMPTS; i++) {
// Send ping
if (ws->Ping(0, &ping_ticks[i]) == 0) {
// Wait for pong
if (ws->WaitForPingReply(TICKS_PER_SECOND * 3) == 0) {
uint32_t reply_tick;
ws->GetPingReplyTick(&reply_tick);
uint32_t rtt = reply_tick - ping_ticks[i];
rtts[i] = (rtt * 1000.0f) / TICKS_PER_SECOND;
} else {
rtts[i] = -1.0f; // Timeout
}
}
OSTimeDly(TICKS_PER_SECOND);
}
// Analyze results
printf("Ping Attempt Analysis:\n");
for (int i = 0; i < ATTEMPTS; i++) {
if (rtts[i] >= 0) {
printf(" Attempt %d: %.2f ms\n", i + 1, rtts[i]);
} else {
printf(" Attempt %d: TIMEOUT\n", i + 1);
}
}
}
Validate Pong Sequence
bool ValidatePongSequence(WebSocket *ws, uint32_t expected_min_tick) {
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) != 0) {
printf("ERROR: No pong data available\n");
return false;
}
if (reply_tick < expected_min_tick) {
printf("WARNING: Received stale pong (tick %lu < %lu)\n",
reply_tick, expected_min_tick);
return false;
}
printf("Pong sequence valid (tick %lu)\n", reply_tick);
return true;
}

◆ GetState()

WS_State NB::WebSocket::GetState ( )
inline

Get the current connection state of the WebSocket.

Returns
WS_State The current state of the WebSocket connection

This inline function returns the current state of the WebSocket connection. The state indicates where the connection is in its lifecycle, from initial handshake through active communication to closure.

Common WebSocket states include:

  • WS_STATE_CONNECTING: Connection being established, handshake in progress
  • WS_STATE_OPEN: Connection established and ready for communication
  • WS_STATE_CLOSING: Close handshake initiated, waiting for closure
  • WS_STATE_CLOSED: Connection fully closed

Additional states may include:

  • WS_STATE_INIT: Initial state before connection attempt
  • WS_STATE_ERROR: Error state indicating connection failure

Monitoring the state is essential for:

  • Determining if the connection is ready for data transmission
  • Handling connection lifecycle events appropriately
  • Implementing retry logic for failed connections
  • Preventing operations on closed connections
  • State machine implementations
Note
This function is inline for performance efficiency as it's frequently called in connection management code.
The state represents a snapshot at the time of the call and may change immediately afterward due to network events or other operations.
See also
Ping() for checking connection liveness
WaitForPingReply() for verifying active connection
Example Usage

Expand for Example Usage

Examples

Check Connection Ready
WebSocket *ws = GetActiveWebSocket();
if (ws->GetState() == WS_STATE_OPEN) {
// Connection is ready, safe to send data
ws->write("Hello, WebSocket!", 17);
} else {
printf("Connection not ready, state: %d\n", ws->GetState());
}
Wait for Connection to Establish
bool WaitForConnection(WebSocket *ws, uint32_t timeout_seconds) {
uint32_t start_time = Secs;
while ((Secs - start_time) < timeout_seconds) {
WS_State state = ws->GetState();
if (state == WS_STATE_OPEN) {
printf("Connection established\n");
return true;
} else if (state == WS_STATE_CLOSED || state == WS_STATE_ERROR) {
printf("Connection failed, state: %d\n", state);
return false;
}
// Still connecting, wait a bit
OSTimeDly(TICKS_PER_SECOND / 10);
}
printf("Connection timeout\n");
return false;
}
State Machine Implementation
void HandleWebSocketState(WebSocket *ws) {
WS_State state = ws->GetState();
switch (state) {
case WS_STATE_INIT:
printf("Initializing connection...\n");
break;
case WS_STATE_CONNECTING:
printf("Handshake in progress...\n");
break;
case WS_STATE_OPEN:
printf("Connection active\n");
// Handle data transmission
HandleActiveConnection(ws);
break;
case WS_STATE_CLOSING:
printf("Connection closing...\n");
// Cleanup operations
break;
case WS_STATE_CLOSED:
printf("Connection closed\n");
// Attempt reconnection or cleanup
HandleReconnection(ws);
break;
case WS_STATE_ERROR:
printf("Connection error\n");
HandleConnectionError(ws);
break;
default:
printf("Unknown state: %d\n", state);
break;
}
}
Safe Write with State Check
int SafeWebSocketWrite(WebSocket *ws, const char *data, int length) {
WS_State state = ws->GetState();
if (state != WS_STATE_OPEN) {
printf("Cannot write: connection not open (state: %d)\n", state);
return -1;
}
return ws->write(data, length);
}
State Transition Monitoring
void MonitorWebSocketState(WebSocket *ws) {
static WS_State last_state = WS_STATE_INIT;
WS_State current_state = ws->GetState();
if (current_state != last_state) {
printf("State transition: %d -> %d\n", last_state, current_state);
// Log state change
LogStateChange(Secs, last_state, current_state);
last_state = current_state;
}
}
Automatic Reconnection Logic
void AutoReconnectTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
while (1) {
WS_State state = ws->GetState();
if (state == WS_STATE_CLOSED || state == WS_STATE_ERROR) {
printf("Connection lost, attempting reconnection...\n");
// Attempt reconnection
ws->connect("ws://server.com");
// Wait for connection result
OSTimeDly(TICKS_PER_SECOND * 5);
}
OSTimeDly(TICKS_PER_SECOND);
}
}
Connection Health Check
bool IsConnectionHealthy(WebSocket *ws) {
WS_State state = ws->GetState();
if (state == WS_STATE_OPEN) {
// Connection appears open, verify with ping
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) == 0) {
return true;
}
}
printf("Connection unhealthy, state: %d\n", state);
return false;
}
State-based Resource Management
void ManageWebSocketResources(WebSocket *ws) {
WS_State state = ws->GetState();
switch (state) {
case WS_STATE_OPEN:
// Allocate buffers and resources for active connection
AllocateBuffers(ws);
break;
case WS_STATE_CLOSED:
// Release resources when connection is closed
ReleaseBuffers(ws);
break;
default:
// Minimal resources for other states
break;
}
}

◆ GetWebSocketRecord()

static WebSocket * NB::WebSocket::GetWebSocketRecord ( int fd)
static

Find a WebSocket record by its file descriptor.

Parameters
fdThe WebSocket file descriptor to search for
Returns
WebSocket* Pointer to the WebSocket record if found, nullptr otherwise

This function searches through the internal WebSocket registry to locate a WebSocket instance associated with the given file descriptor. It is typically used internally by the WebSocket management system to retrieve the WebSocket object for operations on an active connection.

Note
This is a static function intended for internal use within the WebSocket implementation.
Example Usage

Expand for Example Usage

Examples

Basic Usage
// Assuming you have a file descriptor from a WebSocket event
int ws_fd = 5; // Example file descriptor
if (ws != nullptr) {
// WebSocket found, perform operations
printf("Found WebSocket for fd %d\n", ws_fd);
} else {
// WebSocket not found
printf("No WebSocket found for fd %d\n", ws_fd);
}
Error Handling
int ws_fd = GetWebSocketFdFromEvent(); // Get fd from some event
if (ws == nullptr) {
printf("ERROR: Invalid WebSocket file descriptor: %d\n", ws_fd);
return;
}
// Safe to use ws here
ws->SendText("Connection validated");

◆ GetWriteSpace()

int NB::WebSocket::GetWriteSpace ( )
inline

Get available transmit buffer space in bytes.

Returns
int Number of bytes available in the transmit buffer for writing

This inline function returns the amount of free space currently available in the WebSocket's transmit (TX) buffer. The value indicates how many bytes can be written to the buffer before it becomes full.

This function is useful for:

  • Determining if there's sufficient space before attempting a write
  • Implementing flow control to prevent buffer overflow
  • Monitoring buffer utilization and performance
  • Deciding whether to queue or drop data in high-throughput scenarios

The available space decreases as data is written to the buffer and increases as buffered data is transmitted over the network. The total buffer capacity is fixed at initialization.

Note
This function is inline for performance efficiency, as it's often called in performance-critical paths.
The returned value represents instantaneous available space. It may change immediately after the call as other operations occur.
Warning
In multi-threaded environments, the available space may change between checking this value and attempting to write. Always verify write operation return values.
See also
ws_write() for writing data to the WebSocket
ws_flush() to force transmission of buffered data
Example Usage

Expand for Example Usage

Examples

Check Buffer Space Before Writing
WebSocket *ws = GetActiveWebSocket();
const char *message = "Important data message";
int msg_length = strlen(message);
int available = ws->GetWriteSpace();
if (available >= msg_length) {
// Sufficient space, safe to write
ws_write(ws->GetFd(), message, msg_length);
printf("Message sent (%d bytes, %d bytes remaining)\n",
msg_length, available - msg_length);
} else {
printf("Insufficient buffer space: need %d, have %d\n",
msg_length, available);
}
static int ws_write(int fd, const char *buf, int nbytes)
Write data to a WebSocket connection.
Monitor Buffer Utilization
void MonitorWebSocketBuffer(WebSocket *ws) {
int available = ws->GetWriteSpace();
int total_capacity = GetTxBufferSize(); // Assuming this exists
int used = total_capacity - available;
float usage_percent = (used * 100.0f) / total_capacity;
printf("TX Buffer Status:\n");
printf(" Used: %d bytes\n", used);
printf(" Available: %d bytes\n", available);
printf(" Capacity: %d bytes\n", total_capacity);
printf(" Usage: %.1f%%\n", usage_percent);
if (usage_percent > 90.0f) {
printf(" WARNING: Buffer nearly full!\n");
}
}
Flow Control Implementation
bool WriteWithFlowControl(WebSocket *ws, const char *data, int length) {
const int MIN_BUFFER_SPACE = 256; // Reserve some space
int available = ws->GetWriteSpace();
if (available < MIN_BUFFER_SPACE) {
// Buffer too full, flush first
printf("Buffer low (%d bytes), flushing...\n", available);
ws_flush(ws->GetFd());
// Wait briefly for buffer to drain
OSTimeDly(TICKS_PER_SECOND / 10);
available = ws->GetWriteSpace();
}
if (available >= length) {
ws_write(ws->GetFd(), data, length);
return true;
} else {
printf("ERROR: Still insufficient space after flush\n");
return false;
}
}
static void ws_flush(int fd)
Flush the transmit buffer of a WebSocket connection.
Large Data Transfer with Space Checking
bool SendLargeData(WebSocket *ws, const char *data, int total_length) {
int bytes_sent = 0;
while (bytes_sent < total_length) {
int available = ws->GetWriteSpace();
if (available < 100) {
// Low space, flush and wait
ws_flush(ws->GetFd());
OSTimeDly(TICKS_PER_SECOND / 20);
continue;
}
// Send as much as we can
int to_send = (total_length - bytes_sent);
if (to_send > available) {
to_send = available;
}
int written = ws_write(ws->GetFd(), data + bytes_sent, to_send);
if (written > 0) {
bytes_sent += written;
printf("Sent %d/%d bytes (%d buffer space)\n",
bytes_sent, total_length, ws->GetWriteSpace());
} else {
printf("Write error\n");
return false;
}
}
ws_flush(ws->GetFd());
return true;
}
Adaptive Data Rate Control
void StreamDataWithAdaptiveRate(WebSocket *ws) {
const int HIGH_WATER_MARK = 2048;
const int LOW_WATER_MARK = 512;
uint32_t delay = TICKS_PER_SECOND / 10; // Start at 100ms
while (IsStreaming()) {
char data[256];
int data_length = GenerateStreamData(data, sizeof(data));
int available = ws->GetWriteSpace();
if (available > HIGH_WATER_MARK) {
// Plenty of space, send faster
delay = TICKS_PER_SECOND / 20; // 50ms
} else if (available < LOW_WATER_MARK) {
// Running low, slow down
delay = TICKS_PER_SECOND / 5; // 200ms
ws_flush(ws->GetFd());
}
ws_write(ws->GetFd(), data, data_length);
OSTimeDly(delay);
}
}
Buffer Health Check
typedef enum {
BUFFER_HEALTHY,
BUFFER_WARNING,
BUFFER_CRITICAL
} BufferHealth;
BufferHealth CheckBufferHealth(WebSocket *ws) {
int available = ws->GetWriteSpace();
if (available > 1024) {
return BUFFER_HEALTHY;
} else if (available > 256) {
printf("WARNING: Buffer space low (%d bytes)\n", available);
return BUFFER_WARNING;
} else {
printf("CRITICAL: Buffer nearly full (%d bytes left)\n", available);
return BUFFER_CRITICAL;
}
}
void HandleBufferHealth(WebSocket *ws) {
BufferHealth health = CheckBufferHealth(ws);
switch (health) {
case BUFFER_HEALTHY:
// Normal operation
break;
case BUFFER_WARNING:
// Flush to prevent congestion
ws_flush(ws->GetFd());
break;
case BUFFER_CRITICAL:
// Emergency flush and delay
ws_flush(ws->GetFd());
OSTimeDly(TICKS_PER_SECOND / 4);
break;
}
}
Prioritized Message Sending
typedef enum {
MSG_PRIORITY_HIGH,
MSG_PRIORITY_NORMAL,
MSG_PRIORITY_LOW
} MessagePriority;
bool SendPrioritizedMessage(WebSocket *ws, const char *msg,
int length, MessagePriority priority) {
int available = ws->GetWriteSpace();
// High priority messages get special treatment
if (priority == MSG_PRIORITY_HIGH) {
if (available < length) {
// Force flush for high priority
ws_flush(ws->GetFd());
OSTimeDly(TICKS_PER_SECOND / 10);
available = ws->GetWriteSpace();
}
// Send even if space is tight
ws_write(ws->GetFd(), msg, length);
ws_flush(ws->GetFd());
return true;
}
// Normal/low priority - only send if space available
if (available >= length) {
ws_write(ws->GetFd(), msg, length);
return true;
}
printf("Dropped %s priority message (insufficient space)\n",
priority == MSG_PRIORITY_NORMAL ? "normal" : "low");
return false;
}
Diagnostic Logging
void LogWebSocketBufferState(WebSocket *ws, const char *context) {
int available = ws->GetWriteSpace();
uint32_t timestamp = Secs;
printf("[%lu] %s: TX buffer = %d bytes available\n",
timestamp, context, available);
// Log to persistent storage or diagnostics system
LogToFile(timestamp, context, available);
}
// Usage examples:
LogWebSocketBufferState(ws, "Before large transfer");
SendLargeData(ws, data, data_size);
LogWebSocketBufferState(ws, "After large transfer");

◆ Ping()

int NB::WebSocket::Ping ( uint32_t len,
uint32_t * sentTick )

Send a WebSocket ping frame and record the timestamp.

Parameters
lenLength of optional ping payload data (typically 0 for standard ping)
sentTickPointer to variable to receive the tick count when ping was sent
Returns
int Returns 0 on success, negative value on failure

This function sends a WebSocket ping control frame to the peer and records the system tick count at the time of transmission. Ping frames are used to:

  • Verify the connection is still alive (keepalive mechanism)
  • Measure round-trip time (RTT) when paired with WaitForPingReply()
  • Detect network issues or dead connections
  • Prevent idle connection timeouts

The ping frame may optionally include payload data (up to 125 bytes per WebSocket specification), though a zero-length ping is most common. The peer should respond with a pong frame containing the same payload.

The sentTick parameter receives the system tick count (from TimeTick) at the moment the ping was sent. This timestamp can be used to calculate round-trip time when the corresponding pong is received.

Note
Ping frames are control frames and have priority over data frames in the WebSocket protocol.
According to RFC 6455, the peer must respond to a ping with a pong frame containing identical payload data.
Warning
Sending too many pings in rapid succession may be considered abusive by the peer and could result in connection closure.
See also
WaitForPingReply() to wait for the corresponding pong response
GetPingReplyTick() to retrieve the pong receive timestamp
Example Usage

Expand for Example Usage

Examples

Basic Ping
WebSocket *ws = GetActiveWebSocket();
uint32_t sent_tick;
// Send a ping with no payload
if (ws->Ping(0, &sent_tick) == 0) {
printf("Ping sent at tick %lu\n", sent_tick);
} else {
printf("Failed to send ping\n");
}
Measure Round-Trip Time
float MeasureRoundTripTime(WebSocket *ws) {
uint32_t sent_tick;
// Send ping
if (ws->Ping(0, &sent_tick) != 0) {
printf("Failed to send ping\n");
return -1.0f;
}
// Wait for pong (with 5 second timeout)
if (ws->WaitForPingReply(TICKS_PER_SECOND * 5) == 0) {
uint32_t reply_tick;
ws->GetPingReplyTick(&reply_tick);
// Calculate RTT in milliseconds
uint32_t rtt_ticks = reply_tick - sent_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
printf("Round-trip time: %.2f ms\n", rtt_ms);
return rtt_ms;
} else {
printf("Ping timeout - no pong received\n");
return -1.0f;
}
}
Periodic Keepalive
void KeepaliveTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
uint32_t sent_tick;
while (1) {
if (ws->GetState() == WS_STATE_OPEN) {
// Send keepalive ping every 30 seconds
if (ws->Ping(0, &sent_tick) == 0) {
printf("Keepalive ping sent\n");
} else {
printf("WARNING: Keepalive ping failed\n");
}
}
OSTimeDly(TICKS_PER_SECOND * 30);
}
}
Connection Health Check
bool VerifyConnectionAlive(WebSocket *ws) {
uint32_t sent_tick;
printf("Verifying connection health...\n");
// Send ping
if (ws->Ping(0, &sent_tick) != 0) {
printf("ERROR: Cannot send ping\n");
return false;
}
// Wait for response (3 second timeout)
if (ws->WaitForPingReply(TICKS_PER_SECOND * 3) == 0) {
printf("Connection verified: peer responsive\n");
return true;
} else {
printf("ERROR: Connection appears dead (no pong)\n");
return false;
}
}
Multiple Ping Attempts
bool PingWithRetry(WebSocket *ws, int max_attempts) {
for (int i = 0; i < max_attempts; i++) {
uint32_t sent_tick;
printf("Ping attempt %d/%d\n", i + 1, max_attempts);
if (ws->Ping(0, &sent_tick) == 0) {
// Wait for pong
if (ws->WaitForPingReply(TICKS_PER_SECOND * 2) == 0) {
printf("Pong received on attempt %d\n", i + 1);
return true;
}
}
// Brief delay before retry
OSTimeDly(TICKS_PER_SECOND / 2);
}
printf("All ping attempts failed\n");
return false;
}
Network Quality Monitoring
void MonitorNetworkQuality(WebSocket *ws) {
const int SAMPLE_COUNT = 10;
float rtt_samples[SAMPLE_COUNT];
int successful_pings = 0;
printf("Monitoring network quality (%d samples)...\n", SAMPLE_COUNT);
for (int i = 0; i < SAMPLE_COUNT; i++) {
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) == 0) {
if (ws->WaitForPingReply(TICKS_PER_SECOND * 2) == 0) {
uint32_t reply_tick;
ws->GetPingReplyTick(&reply_tick);
uint32_t rtt_ticks = reply_tick - sent_tick;
rtt_samples[successful_pings] =
(rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
successful_pings++;
}
}
OSTimeDly(TICKS_PER_SECOND);
}
if (successful_pings > 0) {
// Calculate statistics
float avg = 0, min = rtt_samples[0], max = rtt_samples[0];
for (int i = 0; i < successful_pings; i++) {
avg += rtt_samples[i];
if (rtt_samples[i] < min) min = rtt_samples[i];
if (rtt_samples[i] > max) max = rtt_samples[i];
}
avg /= successful_pings;
printf("Network Quality Report:\n");
printf(" Success rate: %d/%d (%.1f%%)\n",
successful_pings, SAMPLE_COUNT,
(successful_pings * 100.0f) / SAMPLE_COUNT);
printf(" RTT: avg=%.2fms, min=%.2fms, max=%.2fms\n",
avg, min, max);
} else {
printf("ERROR: No successful pings\n");
}
}
Ping with Payload
bool SendPingWithData(WebSocket *ws, const char *payload, uint32_t len) {
uint32_t sent_tick;
if (len > 125) {
printf("ERROR: Ping payload too large (max 125 bytes)\n");
return false;
}
// Send ping with custom payload
if (ws->Ping(len, &sent_tick) == 0) {
printf("Ping with %lu byte payload sent\n", len);
return true;
} else {
printf("Failed to send ping\n");
return false;
}
}

◆ Pong()

void NB::WebSocket::Pong ( uint32_t len)

Send a pong frame in response to a ping.

Parameters
lenLength of pong payload data (should match the ping payload length)

This member function sends a WebSocket pong control frame, typically in response to a received ping frame from the peer. According to the WebSocket protocol (RFC 6455), when a ping is received, the endpoint must respond with a pong containing the same payload data.

Pong frames serve multiple purposes:

  • Acknowledge receipt of ping frames (keepalive confirmation)
  • Enable the peer to measure round-trip time
  • Demonstrate connection liveness
  • Satisfy protocol requirements for ping-pong exchanges

The WebSocket implementation typically handles ping frames automatically and sends pong responses without application intervention. However, this function allows manual pong transmission if needed for:

  • Custom ping-pong protocols
  • Testing and debugging
  • Explicit control over pong timing
  • Unsolicited pongs (though not recommended by the spec)

The length parameter should match the payload length of the ping being responded to. Most pings have zero-length payloads, so len=0 is most common. The maximum payload length for control frames is 125 bytes per the WebSocket specification.

Note
In most cases, the WebSocket implementation automatically handles incoming pings and sends pongs without requiring application code to call this function.
Pong frames are control frames and have priority over data frames in the WebSocket protocol.
Per RFC 6455, pong frames may be sent unsolicited, though this is uncommon and not recommended unless you have a specific reason.
Warning
The payload data must match the ping payload. This function sends a pong with the specified length, but the actual payload matching is handled internally by the WebSocket implementation.
See also
Ping() to send ping frames
WaitForPingReply() to wait for pong responses to sent pings
GetPingReplyTick() to retrieve pong reception timestamp
Example Usage

Expand for Example Usage

Examples

Manual Pong Response
WebSocket *ws = GetActiveWebSocket();
// Send pong with no payload (most common case)
ws->Pong(0);
printf("Pong frame sent\n");
Custom Ping Handler
void HandlePingFrame(WebSocket *ws, uint32_t payload_length) {
printf("Received ping with %lu byte payload\n", payload_length);
// Send matching pong
ws->Pong(payload_length);
printf("Pong response sent\n");
}
Delayed Pong for Testing
void SendDelayedPong(WebSocket *ws, uint32_t delay_ms) {
printf("Delaying pong by %lu ms...\n", delay_ms);
// Simulate processing delay
OSTimeDly((delay_ms * TICKS_PER_SECOND) / 1000);
// Send pong
ws->Pong(0);
printf("Delayed pong sent\n");
}
Pong with Payload
void SendPongWithPayload(WebSocket *ws, const char *payload, uint32_t len) {
if (len > 125) {
printf("ERROR: Pong payload too large (max 125 bytes)\n");
return;
}
// Note: This example assumes the WebSocket implementation
// has been configured with the payload data to echo
ws->Pong(len);
printf("Pong sent with %lu byte payload\n", len);
}
Monitored Pong Response
void MonitoredPongResponse(WebSocket *ws, uint32_t ping_received_tick) {
uint32_t current_tick = TimeTick;
uint32_t processing_time = current_tick - ping_received_tick;
// Send pong
ws->Pong(0);
float processing_ms = (processing_time * 1000.0f) / TICKS_PER_SECOND;
printf("Pong sent (processing time: %.3f ms)\n", processing_ms);
}
Conditional Pong Sending
void ConditionalPong(WebSocket *ws, bool should_respond) {
if (should_respond) {
ws->Pong(0);
printf("Pong sent in response to ping\n");
} else {
printf("Pong suppressed (testing mode)\n");
}
}
Unsolicited Pong (Heartbeat)
void SendUnsolicitedPong(WebSocket *ws) {
// Per RFC 6455, unsolicited pongs are allowed
// (though uncommon in practice)
ws->Pong(0);
printf("Unsolicited pong sent as heartbeat\n");
}
void HeartbeatTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
while (1) {
if (ws->GetState() == WS_STATE_OPEN) {
SendUnsolicitedPong(ws);
}
// Send heartbeat every 30 seconds
OSTimeDly(TICKS_PER_SECOND * 30);
}
}
Pong Statistics Tracking
static uint32_t pong_count = 0;
void SendTrackedPong(WebSocket *ws) {
ws->Pong(0);
pong_count++;
printf("Pong sent (total: %lu)\n", pong_count);
// Log every 100 pongs
if (pong_count % 100 == 0) {
printf("Milestone: %lu pongs sent\n", pong_count);
}
}
State-Aware Pong Handling
void SafePongResponse(WebSocket *ws) {
WS_State state = ws->GetState();
if (state == WS_STATE_OPEN) {
ws->Pong(0);
printf("Pong sent (connection open)\n");
} else {
printf("Cannot send pong: connection not open (state: %d)\n", state);
}
}
Diagnostic Pong with Logging
void DiagnosticPong(WebSocket *ws, const char *context) {
uint32_t timestamp = Secs;
uint32_t send_tick = TimeTick;
// Send pong
ws->Pong(0);
// Log the event
printf("[%lu] Pong sent in context: %s (tick: %lu)\n",
timestamp, context, send_tick);
// Could also log to persistent storage
LogEvent(timestamp, "PONG_SENT", context, send_tick);
}
Automatic Ping-Pong Handler
// Note: This is typically handled automatically by the WebSocket library
// This example shows what happens internally
void OnPingReceived(WebSocket *ws, const uint8_t *payload, uint32_t len) {
printf("Ping received with %lu byte payload\n", len);
// Store payload for echo in pong (done internally)
// SetPongPayload(payload, len);
// Send pong with matching length
ws->Pong(len);
printf("Automatic pong response sent\n");
}
Throttled Pong Responses
void ThrottledPong(WebSocket *ws) {
static uint32_t last_pong_tick = 0;
const uint32_t MIN_PONG_INTERVAL = TICKS_PER_SECOND / 10; // 100ms
uint32_t current_tick = TimeTick;
if ((current_tick - last_pong_tick) >= MIN_PONG_INTERVAL) {
ws->Pong(0);
last_pong_tick = current_tick;
printf("Pong sent\n");
} else {
printf("Pong throttled (too soon after last pong)\n");
}
}

◆ promote_tcp_ws()

static int NB::WebSocket::promote_tcp_ws ( int tcp_fd)
static

Promote a TCP socket to a WebSocket connection after successful handshake.

Parameters
tcp_fdThe TCP socket file descriptor to promote
Returns
int Returns 0 on success, negative value on failure

This function converts an existing TCP connection into a WebSocket connection after the WebSocket handshake has been completed. It performs the necessary protocol upgrade and registers the connection in the WebSocket management system.

The promotion process typically involves:

  • Validating the WebSocket handshake headers
  • Switching the socket protocol from HTTP to WebSocket
  • Registering the new WebSocket connection
  • Configuring appropriate callbacks for WebSocket events
Note
This function should only be called after a successful WebSocket handshake. Calling it on an invalid or non-handshaked connection will fail.
Warning
The original TCP socket should not be used directly after promotion. All further communication must use WebSocket APIs.
Example Usage

Expand for Example Usage

Examples

Basic Promotion
// After receiving WebSocket handshake on TCP connection
int tcp_fd = AcceptTcpConnection(); // Accept incoming TCP connection
// ... validate WebSocket handshake headers ...
// Promote TCP socket to WebSocket
int result = promote_tcp_ws(tcp_fd);
if (result == 0) {
printf("Successfully promoted TCP socket %d to WebSocket\n", tcp_fd);
} else {
printf("Failed to promote socket %d, error: %d\n", tcp_fd, result);
close(tcp_fd);
}
static int promote_tcp_ws(int tcp_fd)
Promote a TCP socket to a WebSocket connection after successful handshake.
HTTP Upgrade Handler
void HandleWebSocketUpgrade(int tcp_fd, HTTP_Request &request) {
// Verify this is a WebSocket upgrade request
if (request.GetHeader("Upgrade") != "websocket") {
SendHttpError(tcp_fd, 400, "Bad Request");
return;
}
// Send handshake response
SendWebSocketHandshakeResponse(tcp_fd, request);
// Promote to WebSocket
if (promote_tcp_ws(tcp_fd) == 0) {
printf("WebSocket connection established\n");
// WebSocket is now ready for use
} else {
printf("ERROR: Failed to promote connection\n");
close(tcp_fd);
}
}
Complete Server Example
void WebSocketServerTask(void *pd) {
int listen_fd = CreateTcpListenSocket(8080);
while (1) {
int tcp_fd = accept(listen_fd, nullptr, nullptr);
if (tcp_fd > 0) {
// Read HTTP upgrade request
if (ValidateWebSocketHandshake(tcp_fd)) {
// Handshake valid, promote to WebSocket
if (promote_tcp_ws(tcp_fd) == 0) {
printf("New WebSocket client connected\n");
// Connection now managed by WebSocket system
} else {
printf("Failed to create WebSocket\n");
close(tcp_fd);
}
} else {
// Not a valid WebSocket request
SendHttpError(tcp_fd, 400);
close(tcp_fd);
}
}
OSTimeDly(TICKS_PER_SECOND / 10);
}
}
int accept(int listening_socket, IPADDR *address, uint16_t *port, uint16_t timeout)
Accept an incoming connection on a listening socket.

◆ ReadData()

int NB::WebSocket::ReadData ( char * buf,
int nbytes )

Read data from the WebSocket connection.

Parameters
bufPointer to buffer where received data will be stored
nbytesMaximum number of bytes to read into the buffer
Returns
int Number of bytes actually read on success, 0 if no data available, negative value on error or connection closed

This member function reads data from the WebSocket connection into the provided buffer. It handles WebSocket frame decoding automatically, presenting the application data to the caller without protocol overhead.

Key characteristics:

  • Automatic WebSocket frame parsing and payload extraction
  • Handles fragmented messages transparently
  • Processes control frames (ping, pong, close) internally
  • Non-blocking operation when no data is available
  • Removes WebSocket framing and masking

The function operates in non-blocking mode:

  • Returns immediately with available data
  • Returns 0 if no data is currently available
  • Returns negative value on error or connection closure

Return value interpretation:

  • Positive value: Number of bytes read successfully
  • Zero: No data currently available (not an error)
  • Negative: Error occurred or connection closed
Note
The buffer must be large enough to hold the requested number of bytes. Reading less than the available data will leave remaining data for subsequent reads.
This function performs non-blocking reads. For blocking reads with timeout, use ReadDataWithTimeout() or similar if available.
Control frames (ping, pong, close) are typically handled automatically and not returned to the application.
Warning
Always check the return value. A return of 0 or negative indicates no data was read, which requires different handling than successful reads.
The buffer pointer must remain valid and the buffer must be large enough to hold nbytes.
See also
WriteData() for sending data over the WebSocket
GetState() to check connection state before reading
Example Usage

Expand for Example Usage

Examples

Basic Read
WebSocket *ws = GetActiveWebSocket();
char buffer[512];
int bytes_read = ws->ReadData(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // Null terminate for string operations
printf("Received %d bytes: %s\n", bytes_read, buffer);
} else if (bytes_read == 0) {
printf("No data available\n");
} else {
printf("Error reading data\n");
}
Read with Error Handling
int SafeRead(WebSocket *ws, char *buffer, int buffer_size) {
if (buffer == nullptr || buffer_size <= 0) {
printf("ERROR: Invalid buffer parameters\n");
return -1;
}
if (ws->GetState() != WS_STATE_OPEN) {
printf("ERROR: Connection not open\n");
return -1;
}
int bytes_read = ws->ReadData(buffer, buffer_size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
return bytes_read;
} else if (bytes_read == 0) {
// No data available, not an error
return 0;
} else {
// Connection error or closed
printf("Read error, closing connection\n");
return -1;
}
}
Continuous Reading Loop
void ReadTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
char buffer[1024];
while (1) {
int bytes_read = ws->ReadData(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received: %s\n", buffer);
// Process the received data
ProcessMessage(buffer, bytes_read);
} else if (bytes_read < 0) {
printf("Connection error or closed\n");
break;
}
// Brief delay to prevent CPU spinning
OSTimeDly(TICKS_PER_SECOND / 100);
}
printf("Read task exiting\n");
}
JSON Message Reading
bool ReadJsonMessage(WebSocket *ws, char *json_buffer, int buffer_size) {
int bytes_read = ws->ReadData(json_buffer, buffer_size - 1);
if (bytes_read > 0) {
json_buffer[bytes_read] = '\0';
// Validate it's JSON
if (json_buffer[0] == '{' || json_buffer[0] == '[') {
printf("Received JSON: %s\n", json_buffer);
return true;
} else {
printf("WARNING: Non-JSON data received\n");
return false;
}
}
return false;
}
Binary Data Reading
int ReadBinaryFrame(WebSocket *ws, uint8_t *data_buffer, int max_size) {
int bytes_read = ws->ReadData((char *)data_buffer, max_size);
if (bytes_read > 0) {
printf("Received %d bytes of binary data:\n", bytes_read);
// Display as hex
for (int i = 0; i < bytes_read; i++) {
printf("%02X ", data_buffer[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n");
}
return bytes_read;
}
Multi-Buffer Reading (Large Messages)
void ReadLargeMessage(WebSocket *ws) {
const int CHUNK_SIZE = 256;
char chunk[CHUNK_SIZE];
int total_bytes = 0;
printf("Reading large message in chunks...\n");
while (1) {
int bytes_read = ws->ReadData(chunk, CHUNK_SIZE - 1);
if (bytes_read > 0) {
chunk[bytes_read] = '\0';
total_bytes += bytes_read;
// Process this chunk
ProcessChunk(chunk, bytes_read);
} else if (bytes_read == 0) {
// No more data available
break;
} else {
printf("ERROR: Read failed\n");
break;
}
}
printf("Total bytes read: %d\n", total_bytes);
}
Read with Manual Timeout
int ReadWithTimeout(WebSocket *ws, char *buffer, int buffer_size,
uint32_t timeout_ms) {
uint32_t start_tick = TimeTick;
uint32_t timeout_ticks = (timeout_ms * TICKS_PER_SECOND) / 1000;
while (1) {
int bytes_read = ws->ReadData(buffer, buffer_size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
return bytes_read;
} else if (bytes_read < 0) {
// Error
return -1;
}
// Check timeout
if ((TimeTick - start_tick) > timeout_ticks) {
printf("Read timeout after %lu ms\n", timeout_ms);
return 0; // Timeout
}
OSTimeDly(TICKS_PER_SECOND / 100);
}
}
int ReadWithTimeout(int fd, char *buf, int nbytes, unsigned long timeout)
Read data from a file descriptor(fd), or return if at least one byte is not read within the specified...
Command Processing Pattern
void ProcessCommands(WebSocket *ws) {
char command[256];
while (1) {
int bytes_read = ws->ReadData(command, sizeof(command) - 1);
if (bytes_read > 0) {
command[bytes_read] = '\0';
// Process command and generate response
char response[256];
HandleCommand(command, response, sizeof(response));
// Send response
ws->WriteData(response, strlen(response));
ws->Flush();
} else if (bytes_read < 0) {
break;
}
OSTimeDly(TICKS_PER_SECOND / 50);
}
}
Echo Server Implementation
void EchoServerTask(WebSocket *ws) {
char buffer[512];
printf("Echo server started\n");
while (1) {
int bytes_read = ws->ReadData(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Echo: %s\n", buffer);
// Echo back
int bytes_written = ws->WriteData(buffer, bytes_read);
if (bytes_written > 0) {
ws->Flush();
}
} else if (bytes_read < 0) {
printf("Connection closed\n");
break;
}
OSTimeDly(TICKS_PER_SECOND / 100);
}
}
Message Queue Implementation
#define MAX_MESSAGES 10
typedef struct {
char data[256];
int length;
} Message;
Message message_queue[MAX_MESSAGES];
int queue_head = 0;
int queue_tail = 0;
void ReadToQueue(WebSocket *ws) {
char buffer[256];
int bytes_read = ws->ReadData(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
// Add to queue if space available
int next_tail = (queue_tail + 1) % MAX_MESSAGES;
if (next_tail != queue_head) {
memcpy(message_queue[queue_tail].data, buffer, bytes_read);
message_queue[queue_tail].length = bytes_read;
queue_tail = next_tail;
printf("Message queued (%d in queue)\n",
(queue_tail - queue_head + MAX_MESSAGES) % MAX_MESSAGES);
} else {
printf("WARNING: Message queue full, dropping message\n");
}
}
}
Statistical Monitoring
void MonitorReadStatistics(WebSocket *ws) {
static uint32_t total_bytes = 0;
static uint32_t message_count = 0;
static uint32_t start_time = 0;
if (start_time == 0) {
start_time = Secs;
}
char buffer[1024];
int bytes_read = ws->ReadData(buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
total_bytes += bytes_read;
message_count++;
uint32_t elapsed = Secs - start_time;
if (elapsed > 0) {
float bytes_per_sec = total_bytes / (float)elapsed;
float msgs_per_sec = message_count / (float)elapsed;
printf("Stats: %lu bytes (%lu msgs) in %lu sec "
"(%.1f B/s, %.1f msg/s)\n",
total_bytes, message_count, elapsed,
bytes_per_sec, msgs_per_sec);
}
}
}

◆ RunSkippedCallBack()

static void NB::WebSocket::RunSkippedCallBack ( )
static

Executes the callback function when a scheduled run is skipped.

Description

This static function is invoked when a scheduled task or operation cannot be executed at its intended time (e.g., due to system overload, timing conflicts, or the previous execution still running). It allows the system to handle skip events gracefully by notifying registered handlers.

Usage Notes

  • This is a static member function and does not require an object instance
  • Typically called internally by the scheduler when a run is skipped
  • Should be lightweight to avoid further timing issues
  • May log skip events or increment skip counters for monitoring

Thread Safety

Implementation should be thread-safe if called from multiple scheduler contexts.

See Also

  • Related scheduling functions
  • Skip event handling documentation

Expand for Example Usage

Examples

Example 1: Basic Skip Callback Registration
// In your scheduler class
class TaskScheduler {
private:
static void RunSkippedCallBack() {
printf("Task execution was skipped\r\n");
}
public:
void RegisterSkipHandler() {
// Register the callback with scheduler
SetSkipCallback(RunSkippedCallBack);
}
};
Example 2: Skip Tracking Implementation
class PeriodicTask {
private:
static uint32_t skipCount;
static void RunSkippedCallBack() {
skipCount++;
printf("Task skipped (total: %lu)\r\n", skipCount);
// Log if skip count is high
if (skipCount > 10) {
printf("WARNING: High skip count detected\r\n");
}
}
public:
static uint32_t GetSkipCount() { return skipCount; }
};
uint32_t PeriodicTask::skipCount = 0;
Example 3: Detailed Skip Event Handling
class MonitoredTask {
private:
static void RunSkippedCallBack() {
uint32_t currentTime = Secs;
// Log skip event with timestamp
printf("[%lu] Task execution skipped\r\n", currentTime);
// Record in event log
LogEvent("TASK_SKIP", currentTime);
// Optionally trigger recovery action
if (GetSystemLoad() < 50) {
printf("System load normal, attempting retry\r\n");
ScheduleRetry();
}
}
};

◆ setoption()

int NB::WebSocket::setoption ( int option)

Set a socket option on this WebSocket connection.

Parameters
optionThe socket option to set (e.g., WS_SO_TEXT, WS_SO_BINARY, SO_KEEPALIVE, TCP_NODELAY)
Returns
int Returns 0 on success, negative value on failure

This member function sets a specific socket option on the WebSocket connection. Options control various aspects of the WebSocket behavior and the underlying TCP socket configuration.

Common WebSocket-specific options:

  • WS_SO_TEXT: Treat data as text frames (default)
  • WS_SO_BINARY: Treat data as binary frames
  • WS_SO_COMPRESS: Enable per-message compression (if supported)

Common TCP socket options:

  • SO_KEEPALIVE: Enable TCP keepalive packets
  • TCP_NODELAY: Disable Nagle's algorithm for low-latency
  • SO_REUSEADDR: Allow address reuse
  • SO_SNDBUF/SO_RCVBUF: Adjust send/receive buffer sizes

Options can be combined using bitwise OR to set multiple options simultaneously. Setting an already-set option has no adverse effect.

Note
Options should be set before or shortly after connection establishment for best results. Some options may not take effect if changed mid-connection.
Warning
Setting incompatible options (e.g., both WS_SO_TEXT and WS_SO_BINARY) may result in undefined behavior. The last option set typically takes precedence.
See also
clroption() for clearing socket options
getoption() for querying current option state
Example Usage

Expand for Example Usage

Examples

Set Binary Mode
WebSocket *ws = new WebSocket("ws://example.com");
// Enable binary frame mode for sending binary data
if (ws->setoption(WS_SO_BINARY) == 0) {
printf("Binary mode enabled\n");
// Now send binary data
uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
ws->write((char *)data, sizeof(data));
} else {
printf("Failed to enable binary mode\n");
}
Enable TCP Keepalive
WebSocket *ws = GetActiveWebSocket();
// Enable keepalive to detect dead connections
if (ws->setoption(SO_KEEPALIVE) == 0) {
printf("TCP keepalive enabled\n");
} else {
printf("Failed to enable keepalive\n");
}
Configure for Low Latency
void ConfigureLowLatencyWebSocket(WebSocket *ws) {
// Disable Nagle's algorithm for immediate packet transmission
if (ws->setoption(TCP_NODELAY) == 0) {
printf("Low latency mode enabled\n");
} else {
printf("WARNING: Could not enable TCP_NODELAY\n");
}
}
Set Multiple Options
WebSocket *ws = new WebSocket("ws://realtime-server.com");
// Configure for real-time binary streaming
int result = 0;
// Set binary mode
result |= ws->setoption(WS_SO_BINARY);
// Enable low latency
result |= ws->setoption(TCP_NODELAY);
// Enable keepalive
result |= ws->setoption(SO_KEEPALIVE);
if (result == 0) {
printf("All options set successfully\n");
} else {
printf("WARNING: Some options failed to set\n");
}
Switch Between Text and Binary Modes
void SwitchToBinaryMode(WebSocket *ws) {
// Clear text mode if set
ws->clroption(WS_SO_TEXT);
// Set binary mode
if (ws->setoption(WS_SO_BINARY) == 0) {
printf("Switched to binary mode\n");
}
}
void SwitchToTextMode(WebSocket *ws) {
// Clear binary mode if set
ws->clroption(WS_SO_BINARY);
// Set text mode
if (ws->setoption(WS_SO_TEXT) == 0) {
printf("Switched to text mode\n");
}
}
Conditional Option Setting
void ConfigureWebSocketForDataType(WebSocket *ws, bool is_binary) {
if (is_binary) {
ws->setoption(WS_SO_BINARY);
printf("Configured for binary data\n");
} else {
ws->setoption(WS_SO_TEXT);
printf("Configured for text data\n");
}
}
Production vs Development Configuration
void ConfigureWebSocket(WebSocket *ws, bool production_mode) {
if (production_mode) {
// Production: optimize for reliability
ws->setoption(SO_KEEPALIVE);
printf("Production configuration applied\n");
} else {
// Development: optimize for debugging
ws->setoption(TCP_NODELAY);
printf("Development configuration applied\n");
}
}
Enable Compression (if supported)
WebSocket *ws = new WebSocket("ws://example.com");
// Try to enable per-message compression
if (ws->setoption(WS_SO_COMPRESS) == 0) {
printf("Compression enabled - reduced bandwidth usage\n");
} else {
printf("Compression not available or not supported\n");
}

◆ WaitForPingReply()

int NB::WebSocket::WaitForPingReply ( uint32_t timeout)

Block waiting for a pong reply to a previously sent ping.

Parameters
timeoutTimeout value in system ticks (use TICKS_PER_SECOND for conversion)
Returns
int Returns 0 if pong received within timeout, negative value on timeout or error

This function blocks the calling task until a pong frame is received in response to a previously sent ping, or until the specified timeout expires. It is typically called immediately after Ping() to wait for the peer's response.

The function is useful for:

  • Measuring round-trip time with known timeout bounds
  • Verifying connection responsiveness
  • Implementing synchronous ping-pong interactions
  • Detecting dead or slow connections

The timeout parameter is specified in system ticks. Use TICKS_PER_SECOND to convert from seconds:

  • 1 second: TICKS_PER_SECOND
  • 5 seconds: TICKS_PER_SECOND * 5
  • 500 ms: TICKS_PER_SECOND / 2

Return value interpretation:

  • 0: Pong received successfully within timeout
  • Negative: Timeout expired or error occurred
Note
This function blocks the calling task. Ensure the timeout is appropriate for your application to avoid excessive blocking.
A pong should typically be received within a few hundred milliseconds on a healthy network. Timeouts of 2-5 seconds are common.
Warning
Do not call this function without first calling Ping(). The behavior is undefined if no ping has been sent.
Very long timeouts may make the application appear unresponsive.
See also
Ping() to send the ping frame that this function waits for
GetPingReplyTick() to retrieve the exact pong receive timestamp
Example Usage

Expand for Example Usage

Examples

Basic Ping-Pong Sequence
WebSocket *ws = GetActiveWebSocket();
uint32_t sent_tick;
// Send ping
if (ws->Ping(0, &sent_tick) == 0) {
printf("Ping sent, waiting for pong...\n");
// Wait up to 3 seconds for pong
if (ws->WaitForPingReply(TICKS_PER_SECOND * 3) == 0) {
printf("Pong received!\n");
} else {
printf("Timeout: No pong received\n");
}
}
Measure Exact Round-Trip Time
void MeasurePingLatency(WebSocket *ws) {
uint32_t sent_tick;
// Send ping and record timestamp
if (ws->Ping(0, &sent_tick) != 0) {
printf("Failed to send ping\n");
return;
}
// Wait for pong (5 second timeout)
int result = ws->WaitForPingReply(TICKS_PER_SECOND * 5);
if (result == 0) {
uint32_t reply_tick;
ws->GetPingReplyTick(&reply_tick);
// Calculate precise RTT
uint32_t rtt_ticks = reply_tick - sent_tick;
float rtt_ms = (rtt_ticks * 1000.0f) / TICKS_PER_SECOND;
printf("Latency: %.3f ms\n", rtt_ms);
} else {
printf("Ping timeout - connection may be dead\n");
}
}
Connection Liveness Test
bool TestConnectionLiveness(WebSocket *ws) {
uint32_t sent_tick;
printf("Testing connection liveness...\n");
// Send ping
if (ws->Ping(0, &sent_tick) != 0) {
printf("ERROR: Cannot send ping\n");
return false;
}
// Short timeout for quick response detection
int result = ws->WaitForPingReply(TICKS_PER_SECOND * 2);
if (result == 0) {
printf("Connection is ALIVE\n");
return true;
} else {
printf("Connection is DEAD or SLOW\n");
return false;
}
}
Multiple Timeout Strategy
bool SmartPingWait(WebSocket *ws) {
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) != 0) {
return false;
}
// First try short timeout (fast network)
if (ws->WaitForPingReply(TICKS_PER_SECOND) == 0) {
printf("Fast response received\n");
return true;
}
printf("No quick response, waiting longer...\n");
// Try longer timeout (slow network)
if (ws->WaitForPingReply(TICKS_PER_SECOND * 5) == 0) {
printf("Slow response received\n");
return true;
}
printf("No response - connection timeout\n");
return false;
}
Periodic Health Monitoring
void HealthMonitorTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
int consecutive_failures = 0;
const int MAX_FAILURES = 3;
while (1) {
if (ws->GetState() == WS_STATE_OPEN) {
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) == 0) {
int result = ws->WaitForPingReply(TICKS_PER_SECOND * 3);
if (result == 0) {
// Success - reset failure counter
consecutive_failures = 0;
printf("Health check passed\n");
} else {
// Timeout
consecutive_failures++;
printf("Health check failed (%d/%d)\n",
consecutive_failures, MAX_FAILURES);
if (consecutive_failures >= MAX_FAILURES) {
printf("Connection declared dead, closing\n");
ws->close();
break;
}
}
}
}
// Check every 30 seconds
OSTimeDly(TICKS_PER_SECOND * 30);
}
}
Non-blocking Alternative Pattern
// For comparison - non-blocking approach
void NonBlockingPingCheck(WebSocket *ws) {
static uint32_t ping_sent_tick = 0;
static bool ping_pending = false;
if (!ping_pending) {
// Send new ping
if (ws->Ping(0, &ping_sent_tick) == 0) {
ping_pending = true;
printf("Ping sent\n");
}
} else {
// Check if pong received
uint32_t reply_tick;
if (ws->GetPingReplyTick(&reply_tick) == 0) {
if (reply_tick > ping_sent_tick) {
// Pong received
uint32_t rtt = reply_tick - ping_sent_tick;
printf("Pong received, RTT: %lu ticks\n", rtt);
ping_pending = false;
}
}
// Check for timeout
if ((TimeTick - ping_sent_tick) > (TICKS_PER_SECOND * 5)) {
printf("Ping timeout\n");
ping_pending = false;
}
}
}
Connection Quality Assessment
typedef enum {
QUALITY_EXCELLENT, // < 50ms
QUALITY_GOOD, // 50-200ms
QUALITY_FAIR, // 200-500ms
QUALITY_POOR // > 500ms or timeout
} ConnectionQuality;
ConnectionQuality AssessConnectionQuality(WebSocket *ws) {
uint32_t sent_tick;
if (ws->Ping(0, &sent_tick) != 0) {
return QUALITY_POOR;
}
if (ws->WaitForPingReply(TICKS_PER_SECOND * 2) != 0) {
// Timeout
return QUALITY_POOR;
}
uint32_t reply_tick;
ws->GetPingReplyTick(&reply_tick);
float rtt_ms = ((reply_tick - sent_tick) * 1000.0f) / TICKS_PER_SECOND;
if (rtt_ms < 50.0f) {
return QUALITY_EXCELLENT;
} else if (rtt_ms < 200.0f) {
return QUALITY_GOOD;
} else if (rtt_ms < 500.0f) {
return QUALITY_FAIR;
} else {
return QUALITY_POOR;
}
}

◆ WriteData()

int NB::WebSocket::WriteData ( const char * buf,
int nbytes )

Write data to the WebSocket connection.

Parameters
bufPointer to buffer containing data to write
nbytesNumber of bytes to write from the buffer
Returns
int Number of bytes actually written on success, negative value on error

This member function writes data to the WebSocket connection with automatic frame encoding. The function handles the WebSocket protocol overhead, allowing the application to send raw data that will be properly framed for transmission.

Key characteristics:

  • Automatically creates WebSocket frames with appropriate headers
  • Applies masking if required by protocol direction (client to server)
  • Handles message fragmentation for large payloads automatically
  • Buffers data for efficient transmission
  • Respects current frame type setting (text or binary mode)

The function operates according to the WebSocket options set via setoption():

  • WS_SO_TEXT: Data is sent as text frames (default)
  • WS_SO_BINARY: Data is sent as binary frames

Return value interpretation:

  • Positive value: Number of bytes successfully queued for transmission
  • Zero: No bytes written (unusual, may indicate buffer full)
  • Negative: Error occurred (connection closed, invalid parameters, etc.)
Note
The function may return successfully even if data hasn't been physically transmitted yet. Use Flush() to ensure immediate transmission.
For large messages, the function may fragment data into multiple WebSocket frames automatically according to the protocol specification.
Written data is buffered in the transmit buffer. Check GetWriteSpace() before writing if buffer space is a concern.
Warning
Always check the return value. A negative return indicates the write failed and the connection may need to be closed.
The buffer pointer must remain valid for the duration of the call. Do not pass stack buffers that will go out of scope.
See also
ReadData() for reading data from the WebSocket
Flush() to force immediate transmission of buffered data
GetWriteSpace() to check available buffer space
setoption() to configure text or binary frame mode
Example Usage

Expand for Example Usage

Examples

Basic Text Write
WebSocket *ws = GetActiveWebSocket();
const char *message = "Hello, WebSocket!";
int bytes_written = ws->WriteData(message, strlen(message));
if (bytes_written > 0) {
printf("Sent %d bytes\n", bytes_written);
} else {
printf("Failed to send message, error: %d\n", bytes_written);
}
Write with Error Handling
int SafeWrite(WebSocket *ws, const char *data, int length) {
if (data == nullptr || length <= 0) {
printf("ERROR: Invalid write parameters\n");
return -1;
}
if (ws->GetState() != WS_STATE_OPEN) {
printf("ERROR: Connection not open\n");
return -1;
}
int bytes_written = ws->WriteData(data, length);
if (bytes_written == length) {
// All data written successfully
return bytes_written;
} else if (bytes_written > 0) {
// Partial write
printf("WARNING: Partial write (%d of %d bytes)\n",
bytes_written, length);
return bytes_written;
} else {
// Write failed
printf("ERROR: Write failed\n");
return -1;
}
}
Binary Data Transmission
bool SendBinaryData(WebSocket *ws, const uint8_t *data, int length) {
// Ensure binary mode is enabled
ws->setoption(WS_SO_BINARY);
printf("Sending %d bytes of binary data...\n", length);
int bytes_written = ws->WriteData((const char *)data, length);
if (bytes_written == length) {
printf("Binary data sent successfully\n");
return true;
} else {
printf("ERROR: Binary send failed\n");
return false;
}
}
JSON Message Transmission
bool SendJsonMessage(WebSocket *ws, const char *json_str) {
// Ensure text mode for JSON
ws->setoption(WS_SO_TEXT);
int length = strlen(json_str);
printf("Sending JSON (%d bytes): %s\n", length, json_str);
int bytes_written = ws->WriteData(json_str, length);
if (bytes_written == length) {
ws->Flush(); // Ensure immediate delivery
return true;
} else {
printf("ERROR: JSON send failed\n");
return false;
}
}
Formatted Message Writing
int WriteFormattedMessage(WebSocket *ws, const char *format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
int length = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (length > 0 && length < (int)sizeof(buffer)) {
int result = ws->WriteData(buffer, length);
if (result > 0) {
ws->Flush();
printf("Formatted message sent: %s\n", buffer);
}
return result;
} else {
printf("ERROR: Message too large for buffer\n");
return -1;
}
}
// Usage:
WriteFormattedMessage(ws, "{\"sensor\":\"%s\",\"value\":%.2f}",
"temperature", 23.45);
Large Data Transfer with Chunking
bool WriteLargeData(WebSocket *ws, const char *data, int total_length) {
const int CHUNK_SIZE = 1024;
int bytes_sent = 0;
printf("Sending large payload: %d bytes\n", total_length);
while (bytes_sent < total_length) {
// Check buffer space
int space = ws->GetWriteSpace();
if (space < 256) {
printf("Buffer low, flushing...\n");
ws->Flush();
OSTimeDly(TICKS_PER_SECOND / 10);
}
// Calculate chunk size
int remaining = total_length - bytes_sent;
int to_send = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : remaining;
// Write chunk
int bytes_written = ws->WriteData(data + bytes_sent, to_send);
if (bytes_written > 0) {
bytes_sent += bytes_written;
printf("Progress: %d/%d bytes (%.1f%%)\n",
bytes_sent, total_length,
(bytes_sent * 100.0f) / total_length);
} else {
printf("ERROR: Write failed at %d bytes\n", bytes_sent);
return false;
}
}
ws->Flush();
printf("Large data transfer complete\n");
return true;
}
Streaming Sensor Data
void StreamSensorReadings(WebSocket *ws) {
char buffer[128];
while (IsStreamingEnabled()) {
// Read sensors
float temp = ReadTemperature();
float humidity = ReadHumidity();
float pressure = ReadPressure();
// Format as JSON
snprintf(buffer, sizeof(buffer),
"{\"temp\":%.2f,\"humidity\":%.2f,\"pressure\":%.2f}",
temp, humidity, pressure);
// Write and flush for real-time delivery
int written = ws->WriteData(buffer, strlen(buffer));
if (written > 0) {
ws->Flush();
} else {
printf("WARNING: Failed to send sensor data\n");
}
OSTimeDly(TICKS_PER_SECOND);
}
}
Broadcast to Multiple WebSockets
void BroadcastMessage(const char *message) {
int length = strlen(message);
int success_count = 0;
int fail_count = 0;
printf("Broadcasting: %s\n", message);
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->GetState() == WS_STATE_OPEN) {
int written = ws->WriteData(message, length);
if (written == length) {
ws->Flush();
success_count++;
} else {
fail_count++;
}
}
}
printf("Broadcast complete: %d sent, %d failed\n",
success_count, fail_count);
}
Write with Buffer Space Check
bool WriteWithSpaceCheck(WebSocket *ws, const char *data, int length) {
int available = ws->GetWriteSpace();
if (available < length) {
printf("Insufficient buffer space: need %d, have %d\n",
length, available);
// Flush and retry
ws->Flush();
OSTimeDly(TICKS_PER_SECOND / 10);
available = ws->GetWriteSpace();
if (available < length) {
printf("ERROR: Still insufficient space after flush\n");
return false;
}
}
int written = ws->WriteData(data, length);
return (written == length);
}
Rate-Limited Writing
void WriteWithRateLimit(WebSocket *ws, const char *data, int length,
int max_bytes_per_second) {
int bytes_sent = 0;
uint32_t start_time = Secs;
while (bytes_sent < length) {
// Calculate how much we can send this second
uint32_t elapsed = Secs - start_time;
int allowed = (elapsed + 1) * max_bytes_per_second;
int to_send = allowed - bytes_sent;
if (to_send > (length - bytes_sent)) {
to_send = length - bytes_sent;
}
if (to_send > 0) {
int written = ws->WriteData(data + bytes_sent, to_send);
if (written > 0) {
bytes_sent += written;
}
}
OSTimeDly(TICKS_PER_SECOND / 10);
}
ws->Flush();
printf("Rate-limited transfer complete: %d bytes\n", bytes_sent);
}

◆ ws_clroption()

static int NB::WebSocket::ws_clroption ( int fd,
int option )
static

Clear a socket option on a WebSocket connection.

Parameters
fdThe WebSocket file descriptor
optionThe socket option to clear (e.g., SO_KEEPALIVE, TCP_NODELAY)
Returns
int Returns 0 on success, negative value on failure

This static wrapper function clears (disables) a specific socket option on the underlying socket associated with the WebSocket connection. It is the counterpart to ws_setoption() and allows you to disable previously enabled options.

Clearing an option returns the socket to its default behavior for that particular setting. This is useful when you need to dynamically adjust socket behavior based on runtime conditions or when reverting configuration changes.

Note
Clearing an option that was never set has no effect and typically succeeds.
See also
ws_setoption() for setting socket options
ws_getoption() for querying current option state
Example Usage

Expand for Example Usage

Examples

Disable TCP Keepalive
int ws_fd = GetWebSocketFd();
// Disable keepalive (maybe to reduce network traffic)
if (ws_clroption(ws_fd, SO_KEEPALIVE) == 0) {
printf("Keepalive disabled for WebSocket fd %d\n", ws_fd);
} else {
printf("Failed to disable keepalive\n");
}
static int ws_clroption(int fd, int option)
Clear a socket option on a WebSocket connection.
Re-enable Nagle's Algorithm
// Clear TCP_NODELAY to re-enable Nagle's algorithm
int ws_fd = GetActiveWebSocketFd();
if (ws_clroption(ws_fd, TCP_NODELAY) == 0) {
printf("Nagle's algorithm re-enabled - buffering mode\n");
} else {
printf("ERROR: Could not clear TCP_NODELAY option\n");
}
Dynamic Option Management
void ToggleWebSocketMode(int ws_fd, bool lowLatencyMode) {
if (lowLatencyMode) {
// Enable low-latency settings
ws_setoption(ws_fd, TCP_NODELAY);
printf("Switched to low-latency mode\n");
} else {
// Disable low-latency, allow buffering
ws_clroption(ws_fd, TCP_NODELAY);
printf("Switched to standard mode\n");
}
}
Cleanup Before Reconnection
void ResetWebSocketOptions(int ws_fd) {
// Clear all custom options before reconnecting
ws_clroption(ws_fd, SO_KEEPALIVE);
ws_clroption(ws_fd, TCP_NODELAY);
ws_clroption(ws_fd, SO_REUSEADDR);
printf("WebSocket options reset to defaults\n");
}

◆ ws_externalclose()

static int NB::WebSocket::ws_externalclose ( int fd)
static

Close a WebSocket connection from an external caller.

Parameters
fdThe WebSocket file descriptor to close
Returns
int Returns 0 on success, negative value on failure

This static wrapper function provides a mechanism to close a WebSocket connection from external code (outside the WebSocket class). It performs a clean shutdown of the WebSocket, including:

  • Sending a WebSocket close frame to the peer
  • Flushing any pending data in buffers
  • Releasing associated resources
  • Removing the connection from the WebSocket registry

This function differs from a simple socket close() by properly handling the WebSocket protocol's closing handshake, ensuring graceful disconnection.

Note
After calling this function, the file descriptor should not be used for any further operations.
Warning
Do not call standard close() on a WebSocket file descriptor. Always use this function to ensure proper cleanup of WebSocket resources.
See also
ws_flush() to flush buffers before closing
Example Usage

Expand for Example Usage

Examples

Basic Close
int ws_fd = GetActiveWebSocketFd();
// Close the WebSocket connection
if (ws_externalclose(ws_fd) == 0) {
printf("WebSocket fd %d closed successfully\n", ws_fd);
} else {
printf("Failed to close WebSocket fd %d\n", ws_fd);
}
static int ws_externalclose(int fd)
Close a WebSocket connection from an external caller.
Close with Flush
void CleanCloseWebSocket(int ws_fd) {
// Ensure all pending data is sent before closing
ws_flush(ws_fd);
// Now close the connection
int result = ws_externalclose(ws_fd);
if (result == 0) {
printf("WebSocket cleanly closed\n");
} else {
printf("ERROR: Failed to close WebSocket, code: %d\n", result);
}
}
Timeout-based Close
void CloseIdleWebSockets(uint32_t maxIdleSeconds) {
uint32_t currentTime = Secs;
for (int i = 0; i < MAX_WEBSOCKETS; i++) {
WebSocket *ws = GetWebSocketByIndex(i);
if (ws != nullptr) {
uint32_t idleTime = currentTime - ws->GetLastActivityTime();
if (idleTime > maxIdleSeconds) {
int fd = ws->GetFd();
printf("Closing idle WebSocket fd %d (idle %lu sec)\n",
fd, idleTime);
}
}
}
}
Error Condition Close
void HandleWebSocketError(int ws_fd, int error_code) {
printf("WebSocket error %d on fd %d, closing connection\n",
error_code, ws_fd);
// Attempt to close gracefully
if (ws_externalclose(ws_fd) != 0) {
printf("WARNING: Error during close, connection may not be clean\n");
}
}
Shutdown All WebSockets
void ShutdownAllWebSockets() {
printf("Shutting down all WebSocket connections...\n");
int closed_count = 0;
int error_count = 0;
for (int i = 0; i < MAX_WEBSOCKETS; i++) {
WebSocket *ws = GetWebSocketByIndex(i);
if (ws != nullptr) {
int fd = ws->GetFd();
// Flush before closing
ws_flush(fd);
if (ws_externalclose(fd) == 0) {
closed_count++;
} else {
error_count++;
printf("ERROR: Failed to close WebSocket fd %d\n", fd);
}
}
}
printf("Shutdown complete: %d closed, %d errors\n",
closed_count, error_count);
}
Conditional Close
void CloseWebSocketIfCondition(int ws_fd, bool force) {
if (ws == nullptr) {
printf("Invalid WebSocket fd: %d\n", ws_fd);
return;
}
// Check if there's pending data
if (!force && ws->HasPendingData()) {
printf("WebSocket has pending data, flushing before close\n");
ws_flush(ws_fd);
OSTimeDly(TICKS_PER_SECOND / 10); // Brief delay for flush
}
}

◆ ws_flush()

static void NB::WebSocket::ws_flush ( int fd)
static

Flush the transmit buffer of a WebSocket connection.

Parameters
fdThe WebSocket file descriptor

This static wrapper function forces any pending data in the WebSocket's transmit buffer to be sent immediately to the peer. Normally, the WebSocket implementation may buffer small amounts of data to optimize network usage, but this function ensures immediate transmission.

Common use cases include:

  • Before closing a connection to ensure all data is sent
  • After sending critical messages that need immediate delivery
  • When implementing real-time protocols with strict timing requirements
  • Before entering low-power or idle states
Note
This function blocks until the buffer is flushed or a timeout occurs. The actual transmission completion depends on network conditions and the peer's receive capability.
Warning
Excessive flushing can reduce network efficiency. Use only when immediate transmission is required.
See also
ws_externalclose() which should be called after flushing when closing
Example Usage

Expand for Example Usage

Examples

Basic Flush
int ws_fd = GetActiveWebSocketFd();
// Send some data
SendWebSocketData(ws_fd, "Important message");
// Ensure it's sent immediately
ws_flush(ws_fd);
printf("Data flushed to network\n");
Flush Before Close
void SafeCloseWebSocket(int ws_fd) {
// Send final message
SendWebSocketText(ws_fd, "Goodbye!");
// Ensure message is sent before closing
ws_flush(ws_fd);
// Small delay to allow network transmission
OSTimeDly(TICKS_PER_SECOND / 20);
// Now safe to close
}
Critical Message Delivery
void SendCriticalAlert(int ws_fd, const char *alert_msg) {
// Send alert message
SendWebSocketText(ws_fd, alert_msg);
// Flush immediately - this is critical
ws_flush(ws_fd);
printf("Critical alert sent and flushed: %s\n", alert_msg);
}
Real-time Data Streaming
void StreamSensorData(int ws_fd) {
while (IsStreamingActive()) {
// Read sensor
float temperature = ReadTemperature();
float humidity = ReadHumidity();
// Format and send
char buffer[128];
snprintf(buffer, sizeof(buffer),
"{\"temp\":%.2f,\"humidity\":%.2f}",
temperature, humidity);
SendWebSocketText(ws_fd, buffer);
// Flush for real-time delivery
ws_flush(ws_fd);
// Wait before next reading
OSTimeDly(TICKS_PER_SECOND);
}
}
Batch Send with Flush
void SendBatchData(int ws_fd, const char **messages, int count) {
for (int i = 0; i < count; i++) {
SendWebSocketText(ws_fd, messages[i]);
}
// Flush all messages at once
ws_flush(ws_fd);
printf("Sent and flushed %d messages\n", count);
}
Command-Response Pattern
void HandleWebSocketCommand(int ws_fd, const char *command) {
char response[256];
// Process command
ProcessCommand(command, response, sizeof(response));
// Send response
SendWebSocketText(ws_fd, response);
// Flush to ensure client receives response promptly
ws_flush(ws_fd);
printf("Command processed and response sent\n");
}
Periodic Heartbeat
void WebSocketHeartbeatTask(void *pd) {
int ws_fd = GetWebSocketFd();
while (IsConnected(ws_fd)) {
// Send ping/heartbeat
SendWebSocketPing(ws_fd);
// Flush to ensure timely delivery
ws_flush(ws_fd);
// Wait for next heartbeat interval
OSTimeDly(TICKS_PER_SECOND * 30); // 30 second heartbeat
}
}
Error Recovery
void RecoverFromBufferOverflow(int ws_fd) {
printf("Buffer overflow detected, flushing...\n");
// Attempt to flush buffer
ws_flush(ws_fd);
// Check if connection is still valid
int options = ws_getoption(ws_fd);
if (options < 0) {
printf("Connection lost after flush, closing\n");
} else {
printf("Buffer flushed successfully, connection stable\n");
}
}
static int ws_getoption(int fd)
Get the current socket options for a WebSocket connection.

◆ ws_getoption()

static int NB::WebSocket::ws_getoption ( int fd)
static

Get the current socket options for a WebSocket connection.

Parameters
fdThe WebSocket file descriptor
Returns
int A bitmask of currently enabled socket options, or negative value on failure

This static wrapper function retrieves the current state of socket options for the underlying socket associated with the WebSocket connection. It returns a bitmask where each bit represents a specific socket option's state (enabled/disabled).

The returned value can be tested against specific option flags to determine if particular options are currently enabled. This is useful for:

  • Debugging socket configuration
  • Verifying option settings
  • Conditionally changing options based on current state
  • Logging socket configuration
Note
The exact interpretation of the returned bitmask depends on the underlying socket implementation and available options.
See also
ws_setoption() for setting socket options
ws_clroption() for clearing socket options
Example Usage

Expand for Example Usage

Examples

Query Current Options
int ws_fd = GetWebSocketFd();
int options = ws_getoption(ws_fd);
if (options >= 0) {
printf("Current socket options: 0x%08X\n", options);
} else {
printf("Failed to get socket options\n");
}
Check Specific Option
int ws_fd = GetActiveWebSocketFd();
int options = ws_getoption(ws_fd);
if (options >= 0) {
if (options & SO_KEEPALIVE) {
printf("Keepalive is ENABLED\n");
} else {
printf("Keepalive is DISABLED\n");
}
if (options & TCP_NODELAY) {
printf("Nagle's algorithm is DISABLED (low latency mode)\n");
} else {
printf("Nagle's algorithm is ENABLED (buffering mode)\n");
}
}
Conditional Option Setting
void EnsureKeepaliveEnabled(int ws_fd) {
int options = ws_getoption(ws_fd);
if (options < 0) {
printf("ERROR: Cannot read socket options\n");
return;
}
// Only set keepalive if not already enabled
if (!(options & SO_KEEPALIVE)) {
ws_setoption(ws_fd, SO_KEEPALIVE);
printf("Keepalive was disabled, now enabled\n");
} else {
printf("Keepalive already enabled\n");
}
}
Diagnostic Logging
void LogWebSocketConfiguration(int ws_fd) {
int options = ws_getoption(ws_fd);
if (options < 0) {
printf("ERROR: Could not retrieve socket options\n");
return;
}
printf("=== WebSocket Configuration (fd %d) ===\n", ws_fd);
printf("SO_KEEPALIVE: %s\n", (options & SO_KEEPALIVE) ? "ON" : "OFF");
printf("TCP_NODELAY: %s\n", (options & TCP_NODELAY) ? "ON" : "OFF");
printf("SO_REUSEADDR: %s\n", (options & SO_REUSEADDR) ? "ON" : "OFF");
printf("Raw bitmask: 0x%08X\n", options);
printf("=====================================\n");
}
Option Comparison
void CompareWebSocketOptions(int ws_fd1, int ws_fd2) {
int options1 = ws_getoption(ws_fd1);
int options2 = ws_getoption(ws_fd2);
if (options1 < 0 || options2 < 0) {
printf("ERROR: Could not retrieve options for comparison\n");
return;
}
if (options1 == options2) {
printf("Both WebSockets have identical options\n");
} else {
printf("WebSocket options differ:\n");
printf(" WS1: 0x%08X\n", options1);
printf(" WS2: 0x%08X\n", options2);
printf(" XOR: 0x%08X\n", options1 ^ options2);
}
}

◆ ws_read()

static int NB::WebSocket::ws_read ( int fd,
char * buf,
int nbytes )
static

Read data from a WebSocket connection.

Parameters
fdThe WebSocket file descriptor
bufPointer to buffer where received data will be stored
nbytesMaximum number of bytes to read into the buffer
Returns
int Number of bytes actually read on success, 0 if no data available, negative value on error or connection closed

This static wrapper function reads data from a WebSocket connection into the provided buffer. It handles WebSocket frame decoding automatically, presenting the application data to the caller without protocol overhead.

The function behavior includes:

  • Automatic WebSocket frame parsing and payload extraction
  • Handling of fragmented messages
  • Processing of control frames (ping, pong, close)
  • Non-blocking read when no data is available

Return value interpretation:

  • Positive value: Number of bytes read successfully
  • Zero: No data currently available (non-blocking)
  • Negative: Error occurred or connection closed
Note
The buffer must be large enough to hold the requested number of bytes. Reading less than the available data will leave remaining data for subsequent reads.
Warning
Always check the return value. A return of 0 or negative indicates no data was read, which may require different handling than partial reads.
See also
ws_write() for sending data over the WebSocket
Example Usage

Expand for Example Usage

Examples

Basic Read
int ws_fd = GetActiveWebSocketFd();
char buffer[512];
int bytes_read = ws_read(ws_fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // Null terminate for string operations
printf("Received %d bytes: %s\n", bytes_read, buffer);
} else if (bytes_read == 0) {
printf("No data available\n");
} else {
printf("Error reading from WebSocket\n");
}
static int ws_read(int fd, char *buf, int nbytes)
Read data from a WebSocket connection.
Read with Error Handling
int SafeWebSocketRead(int ws_fd, char *buffer, int buffer_size) {
if (buffer == nullptr || buffer_size <= 0) {
printf("ERROR: Invalid buffer parameters\n");
return -1;
}
int bytes_read = ws_read(ws_fd, buffer, buffer_size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
return bytes_read;
} else if (bytes_read == 0) {
// No data available, not an error
return 0;
} else {
// Connection error or closed
printf("WebSocket read error, closing connection\n");
return -1;
}
}
Continuous Reading Loop
void WebSocketReadTask(void *pd) {
int ws_fd = GetWebSocketFd();
char buffer[1024];
while (1) {
int bytes_read = ws_read(ws_fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received: %s\n", buffer);
// Process the received data
ProcessWebSocketMessage(buffer, bytes_read);
} else if (bytes_read < 0) {
printf("Connection closed or error\n");
break;
}
// Brief delay to prevent CPU spinning
OSTimeDly(TICKS_PER_SECOND / 100);
}
printf("WebSocket read task exiting\n");
}
JSON Message Reading
bool ReadJsonMessage(int ws_fd, char *json_buffer, int buffer_size) {
int bytes_read = ws_read(ws_fd, json_buffer, buffer_size - 1);
if (bytes_read > 0) {
json_buffer[bytes_read] = '\0';
// Validate it's valid JSON
if (json_buffer[0] == '{' || json_buffer[0] == '[') {
printf("Received JSON: %s\n", json_buffer);
return true;
} else {
printf("WARNING: Non-JSON data received\n");
return false;
}
}
return false;
}
Binary Data Reading
int ReadBinaryFrame(int ws_fd, uint8_t *data_buffer, int max_size) {
int bytes_read = ws_read(ws_fd, (char *)data_buffer, max_size);
if (bytes_read > 0) {
printf("Received %d bytes of binary data\n", bytes_read);
// Process binary data
for (int i = 0; i < bytes_read; i++) {
printf("%02X ", data_buffer[i]);
}
printf("\n");
}
return bytes_read;
}
Multi-buffer Reading
void ReadLargeMessage(int ws_fd) {
const int CHUNK_SIZE = 256;
char chunk[CHUNK_SIZE];
int total_bytes = 0;
printf("Reading large message in chunks...\n");
while (1) {
int bytes_read = ws_read(ws_fd, chunk, CHUNK_SIZE - 1);
if (bytes_read > 0) {
chunk[bytes_read] = '\0';
total_bytes += bytes_read;
// Process this chunk
ProcessChunk(chunk, bytes_read);
} else if (bytes_read == 0) {
// No more data available
break;
} else {
printf("ERROR: Read failed\n");
break;
}
}
printf("Total bytes read: %d\n", total_bytes);
}
Timeout-based Reading
int ReadWithTimeout(int ws_fd, char *buffer, int buffer_size,
uint32_t timeout_ms) {
uint32_t start_tick = TimeTick;
uint32_t timeout_ticks = (timeout_ms * TICKS_PER_SECOND) / 1000;
while (1) {
int bytes_read = ws_read(ws_fd, buffer, buffer_size - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
return bytes_read;
} else if (bytes_read < 0) {
// Error
return -1;
}
// Check timeout
if ((TimeTick - start_tick) > timeout_ticks) {
printf("Read timeout after %lu ms\n", timeout_ms);
return 0; // Timeout
}
OSTimeDly(TICKS_PER_SECOND / 100);
}
}

◆ ws_read_notify()

static void NB::WebSocket::ws_read_notify ( int fd)
static

TCP read notification callback invoked when data is available on the WebSocket.

Description

This static callback function is invoked by the NetBurner TCP/IP stack when data becomes available to read on the WebSocket file descriptor. It handles incoming WebSocket frames, processes control frames (ping, pong, close), and delivers data frames to the application layer.

Parameters

Parameters
fdThe file descriptor of the TCP socket with available data

Usage Notes

  • Automatically called by the network stack, not invoked directly by user code
  • Should process all available data to avoid notification storms
  • Must handle partial frames and buffering appropriately
  • Should be non-blocking to prevent stalling the network stack
  • Typically registered via SetSocketReadNotify() or similar

WebSocket Frame Handling

This callback must handle:

  • Data frames (text/binary)
  • Control frames (ping, pong, close)
  • Frame fragmentation
  • Opcode validation

Thread Safety

Called from the network stack context. Ensure thread-safe access to shared resources.

See Also


Expand for Example Usage

Examples

Example 1: Basic Read Notification Setup
class WebSocketHandler {
private:
static void ws_read_notify(int fd) {
char buffer[1024];
int bytesRead = read(fd, buffer, sizeof(buffer));
if (bytesRead > 0) {
printf("Received %d bytes on fd %d\r\n", bytesRead, fd);
ProcessWebSocketFrame(buffer, bytesRead);
}
else if (bytesRead == 0) {
printf("Connection closed on fd %d\r\n", fd);
close(fd);
}
}
public:
void RegisterNotifications(int sockfd) {
// Register read callback with TCP stack
SetSocketReadNotify(sockfd, ws_read_notify);
}
};
int read(int fd, char *buf, int nbytes)
Read data from a file descriptor (fd).
Example 2: WebSocket Frame Processing
static void ws_read_notify(int fd) {
uint8_t frameBuffer[2048];
int bytesRead = read(fd, frameBuffer, sizeof(frameBuffer));
if (bytesRead <= 0) {
HandleSocketError(fd, bytesRead);
return;
}
// Parse WebSocket frame header
bool fin = (frameBuffer[0] & 0x80) != 0;
uint8_t opcode = frameBuffer[0] & 0x0F;
switch (opcode) {
case WS_OPCODE_TEXT:
ProcessTextFrame(frameBuffer, bytesRead);
break;
case WS_OPCODE_BINARY:
ProcessBinaryFrame(frameBuffer, bytesRead);
break;
case WS_OPCODE_PING:
SendPong(fd, frameBuffer, bytesRead);
break;
case WS_OPCODE_CLOSE:
HandleCloseFrame(fd);
break;
}
}
static void ws_read_notify(int fd)
TCP read notification callback invoked when data is available on the WebSocket.
Example 3: Buffered Read with Error Handling
class WebSocketConnection {
private:
static std::map<int, FrameBuffer> readBuffers;
static void ws_read_notify(int fd) {
const int CHUNK_SIZE = 512;
uint8_t chunk[CHUNK_SIZE];
// Read all available data
while (true) {
int bytesRead = read(fd, chunk, CHUNK_SIZE);
if (bytesRead > 0) {
// Append to frame buffer
readBuffers[fd].Append(chunk, bytesRead);
// Try to parse complete frames
while (readBuffers[fd].HasCompleteFrame()) {
WebSocketFrame frame = readBuffers[fd].ExtractFrame();
ProcessFrame(fd, frame);
}
}
else if (bytesRead == 0) {
printf("Connection closed gracefully: fd=%d\r\n", fd);
CleanupConnection(fd);
break;
}
else {
// EWOULDBLOCK means no more data available
if (errno == EWOULDBLOCK) break;
printf("Read error on fd=%d: %d\r\n", fd, errno);
CleanupConnection(fd);
break;
}
}
}
};

◆ ws_readwto()

static int NB::WebSocket::ws_readwto ( int fd,
char * buf,
int nbytes,
int timeout )
static

Read data from a WebSocket connection with timeout.

Parameters
fdThe WebSocket file descriptor
bufPointer to buffer where received data will be stored
nbytesMaximum number of bytes to read into the buffer
timeoutTimeout value in system ticks (use TICKS_PER_SECOND for conversion)
Returns
int Number of bytes actually read on success (positive value), 0 if timeout occurred with no data, negative value on error or connection closed

This static wrapper function reads data from a WebSocket connection with a specified timeout period. Unlike ws_read() which returns immediately if no data is available, this function will wait up to the specified timeout for data to arrive.

The timeout mechanism allows for:

  • Blocking reads with maximum wait time
  • Prevention of indefinite blocking
  • Periodic polling with controlled delays
  • Responsive handling of slow or intermittent connections

Return value interpretation:

  • Positive value: Number of bytes read successfully
  • Zero: Timeout expired with no data received
  • Negative: Error occurred or connection closed

The timeout parameter is specified in system ticks. Use TICKS_PER_SECOND to convert from seconds:

  • 1 second: TICKS_PER_SECOND
  • 500 ms: TICKS_PER_SECOND / 2
  • 100 ms: TICKS_PER_SECOND / 10
Note
The buffer must be large enough to hold the requested number of bytes.
This function may return before the timeout if data becomes available or if an error occurs.
Warning
A timeout of 0 is equivalent to non-blocking mode (immediate return). Very large timeout values may block the calling task for extended periods.
See also
ws_read() for non-blocking read operation
ws_write() for sending data over the WebSocket
Example Usage

Expand for Example Usage

Examples

Basic Read with Timeout
int ws_fd = GetActiveWebSocketFd();
char buffer[512];
// Wait up to 5 seconds for data
int bytes_read = ws_readwto(ws_fd, buffer, sizeof(buffer) - 1,
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Received %d bytes: %s\n", bytes_read, buffer);
} else if (bytes_read == 0) {
printf("Timeout: No data received within 5 seconds\n");
} else {
printf("Error reading from WebSocket\n");
}
static int ws_readwto(int fd, char *buf, int nbytes, int timeout)
Read data from a WebSocket connection with timeout.
Request-Response Pattern
bool SendRequestAndWaitForResponse(int ws_fd, const char *request) {
char response[256];
// Send request
if (ws_write(ws_fd, request, strlen(request)) <= 0) {
printf("Failed to send request\n");
return false;
}
ws_flush(ws_fd);
// Wait up to 10 seconds for response
int bytes_read = ws_readwto(ws_fd, response, sizeof(response) - 1,
if (bytes_read > 0) {
response[bytes_read] = '\0';
printf("Response received: %s\n", response);
return true;
} else if (bytes_read == 0) {
printf("ERROR: Response timeout\n");
return false;
} else {
printf("ERROR: Connection error\n");
return false;
}
}
Polling Loop with Timeout
void WebSocketPollingTask(void *pd) {
int ws_fd = GetWebSocketFd();
char buffer[1024];
while (1) {
// Poll with 1 second timeout
int bytes_read = ws_readwto(ws_fd, buffer, sizeof(buffer) - 1,
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("Data received: %s\n", buffer);
ProcessWebSocketData(buffer, bytes_read);
} else if (bytes_read == 0) {
// Timeout - no data, continue polling
// Could perform other tasks here
} else {
// Error - connection likely closed
printf("Connection error, exiting\n");
break;
}
}
}
Short Timeout for Responsive UI
void CheckWebSocketWithShortTimeout(int ws_fd) {
char buffer[256];
// Very short timeout (100ms) for responsive checking
int bytes_read = ws_readwto(ws_fd, buffer, sizeof(buffer) - 1,
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
UpdateUI(buffer);
} else if (bytes_read == 0) {
// Quick timeout, continue normal operation
} else {
// Connection error
HandleConnectionError();
}
}
Multi-attempt Read with Retry
int ReadWithRetry(int ws_fd, char *buffer, int buffer_size,
int attempts, uint32_t timeout_per_attempt) {
for (int i = 0; i < attempts; i++) {
int bytes_read = ws_readwto(ws_fd, buffer, buffer_size - 1,
timeout_per_attempt);
if (bytes_read > 0) {
// Success
buffer[bytes_read] = '\0';
printf("Read successful on attempt %d\n", i + 1);
return bytes_read;
} else if (bytes_read == 0) {
// Timeout, try again
printf("Attempt %d timed out, retrying...\n", i + 1);
} else {
// Error, don't retry
printf("Read error on attempt %d\n", i + 1);
return -1;
}
}
printf("All %d attempts failed\n", attempts);
return 0; // All attempts timed out
}
// Usage:
char data[512];
// Try 3 times, 2 seconds each
int result = ReadWithRetry(ws_fd, data, sizeof(data), 3,
Blocking Read Until Complete Message
bool ReadCompleteMessage(int ws_fd, char *buffer, int expected_size) {
int total_read = 0;
const int TIMEOUT = TICKS_PER_SECOND * 30; // 30 second total timeout
printf("Reading complete message (%d bytes expected)...\n",
expected_size);
while (total_read < expected_size) {
int remaining = expected_size - total_read;
int bytes_read = ws_readwto(ws_fd, buffer + total_read,
remaining, TIMEOUT);
if (bytes_read > 0) {
total_read += bytes_read;
printf("Progress: %d/%d bytes\n", total_read, expected_size);
} else if (bytes_read == 0) {
printf("ERROR: Timeout waiting for data\n");
return false;
} else {
printf("ERROR: Read error\n");
return false;
}
}
buffer[total_read] = '\0';
printf("Complete message received\n");
return true;
}
Adaptive Timeout Based on Activity
void AdaptiveReadLoop(int ws_fd) {
char buffer[512];
uint32_t timeout = TICKS_PER_SECOND; // Start with 1 second
int consecutive_timeouts = 0;
while (1) {
int bytes_read = ws_readwto(ws_fd, buffer, sizeof(buffer) - 1,
timeout);
if (bytes_read > 0) {
// Data received - reset to short timeout
buffer[bytes_read] = '\0';
ProcessData(buffer, bytes_read);
timeout = TICKS_PER_SECOND;
consecutive_timeouts = 0;
} else if (bytes_read == 0) {
// Timeout - increase timeout for next attempt
consecutive_timeouts++;
if (consecutive_timeouts > 3) {
// Multiple timeouts, extend timeout to 5 seconds
timeout = TICKS_PER_SECOND * 5;
printf("Extended timeout due to inactivity\n");
}
} else {
// Error
printf("Connection error\n");
break;
}
}
}

◆ ws_setoption()

static int NB::WebSocket::ws_setoption ( int fd,
int option )
static

Set a socket option on a WebSocket connection.

Parameters
fdThe WebSocket file descriptor
optionThe socket option to set (e.g., SO_KEEPALIVE, TCP_NODELAY)
Returns
int Returns 0 on success, negative value on failure

This static wrapper function sets a specific socket option on the underlying socket associated with the WebSocket connection. It provides a convenient interface to modify socket behavior without directly accessing the underlying TCP socket.

Common socket options include:

  • SO_KEEPALIVE: Enable TCP keepalive packets
  • TCP_NODELAY: Disable Nagle's algorithm for low-latency
  • SO_REUSEADDR: Allow address reuse
  • SO_SNDBUF/SO_RCVBUF: Adjust send/receive buffer sizes
Note
This function operates on the underlying TCP socket of the WebSocket. Some options may not be applicable or may have unexpected behavior in WebSocket mode.
Warning
Setting incompatible socket options may disrupt WebSocket communication. Use with caution and test thoroughly.
Example Usage

Expand for Example Usage

Examples

Enable TCP Keepalive
WebSocket *ws = new WebSocket("ws://example.com");
int fd = ws->GetFd();
// Enable keepalive to detect dead connections
if (ws_setoption(fd, SO_KEEPALIVE) == 0) {
printf("Keepalive enabled for WebSocket fd %d\n", fd);
} else {
printf("Failed to enable keepalive\n");
}
Disable Nagle's Algorithm
// For low-latency applications, disable Nagle's algorithm
int ws_fd = GetActiveWebSocketFd();
if (ws_setoption(ws_fd, TCP_NODELAY) == 0) {
printf("TCP_NODELAY enabled - reduced latency mode\n");
} else {
printf("ERROR: Could not set TCP_NODELAY option\n");
}
Multiple Options Setup
void ConfigureWebSocketConnection(int ws_fd) {
// Configure multiple socket options for optimal performance
// Enable keepalive
if (ws_setoption(ws_fd, SO_KEEPALIVE) != 0) {
printf("Warning: Could not enable keepalive\n");
}
// Disable Nagle for low latency
if (ws_setoption(ws_fd, TCP_NODELAY) != 0) {
printf("Warning: Could not disable Nagle's algorithm\n");
}
printf("WebSocket connection configured\n");
}

◆ ws_write()

static int NB::WebSocket::ws_write ( int fd,
const char * buf,
int nbytes )
static

Write data to a WebSocket connection.

Parameters
fdThe WebSocket file descriptor
bufPointer to buffer containing data to send
nbytesNumber of bytes to write from the buffer
Returns
int Number of bytes actually written on success, negative value on error

This static wrapper function writes data to a WebSocket connection with automatic WebSocket frame encoding. The function handles the protocol overhead, allowing the application to send raw data that will be properly framed for WebSocket transmission.

The function behavior includes:

  • Automatic WebSocket frame creation with proper headers
  • Masking of data (if required by protocol direction)
  • Handling of message fragmentation for large payloads
  • Buffering and flow control

Return value interpretation:

  • Positive value: Number of bytes successfully queued for transmission
  • Negative: Error occurred (connection closed, buffer full, invalid parameters)
Note
The function may return successfully even if data hasn't been physically transmitted yet. Use ws_flush() to ensure immediate transmission.
For large messages, the function may fragment the data into multiple WebSocket frames automatically.
Warning
Always check the return value. A negative return indicates the write failed and the connection may need to be closed.
See also
ws_read() for reading data from the WebSocket
ws_flush() to force immediate transmission of buffered data
Example Usage

Expand for Example Usage

Examples

Basic Write
int ws_fd = GetActiveWebSocketFd();
const char *message = "Hello, WebSocket!";
int bytes_written = ws_write(ws_fd, message, strlen(message));
if (bytes_written > 0) {
printf("Sent %d bytes\n", bytes_written);
} else {
printf("Failed to send message\n");
}
Write with Error Handling
int SafeWebSocketWrite(int ws_fd, const char *data, int length) {
if (data == nullptr || length <= 0) {
printf("ERROR: Invalid write parameters\n");
return -1;
}
int bytes_written = ws_write(ws_fd, data, length);
if (bytes_written == length) {
// All data written successfully
return bytes_written;
} else if (bytes_written > 0) {
// Partial write
printf("WARNING: Partial write (%d of %d bytes)\n",
bytes_written, length);
return bytes_written;
} else {
// Write failed
printf("ERROR: Write failed, closing connection\n");
return -1;
}
}
Write with Flush
void SendImmediateMessage(int ws_fd, const char *message) {
int length = strlen(message);
int bytes_written = ws_write(ws_fd, message, length);
if (bytes_written > 0) {
// Flush to ensure immediate delivery
ws_flush(ws_fd);
printf("Message sent and flushed: %s\n", message);
} else {
printf("Failed to send message\n");
}
}
JSON Message Transmission
bool SendJsonMessage(int ws_fd, const char *json_str) {
int length = strlen(json_str);
printf("Sending JSON (%d bytes): %s\n", length, json_str);
int bytes_written = ws_write(ws_fd, json_str, length);
if (bytes_written == length) {
ws_flush(ws_fd); // Ensure JSON is sent immediately
return true;
} else {
printf("ERROR: JSON send failed\n");
return false;
}
}
Binary Data Transmission
int SendBinaryData(int ws_fd, const uint8_t *data, int data_length) {
printf("Sending %d bytes of binary data...\n", data_length);
int bytes_written = ws_write(ws_fd, (const char *)data, data_length);
if (bytes_written == data_length) {
printf("Binary data sent successfully\n");
return bytes_written;
} else if (bytes_written > 0) {
printf("WARNING: Only sent %d of %d bytes\n",
bytes_written, data_length);
return bytes_written;
} else {
printf("ERROR: Binary send failed\n");
return -1;
}
}
Formatted Message Transmission
void SendFormattedMessage(int ws_fd, const char *format, ...) {
char buffer[512];
va_list args;
va_start(args, format);
int length = vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
if (length > 0 && length < (int)sizeof(buffer)) {
int bytes_written = ws_write(ws_fd, buffer, length);
if (bytes_written > 0) {
ws_flush(ws_fd);
printf("Formatted message sent: %s\n", buffer);
} else {
printf("Failed to send formatted message\n");
}
} else {
printf("ERROR: Message too large for buffer\n");
}
}
// Usage:
SendFormattedMessage(ws_fd, "{\"sensor\":\"%s\",\"value\":%.2f}",
"temperature", 23.45);
Large Data Transmission
bool SendLargePayload(int ws_fd, const char *data, int total_length) {
const int CHUNK_SIZE = 1024;
int bytes_sent = 0;
printf("Sending large payload: %d bytes\n", total_length);
while (bytes_sent < total_length) {
int remaining = total_length - bytes_sent;
int to_send = (remaining > CHUNK_SIZE) ? CHUNK_SIZE : remaining;
int bytes_written = ws_write(ws_fd, data + bytes_sent, to_send);
if (bytes_written > 0) {
bytes_sent += bytes_written;
printf("Progress: %d/%d bytes\n", bytes_sent, total_length);
} else {
printf("ERROR: Write failed at %d bytes\n", bytes_sent);
return false;
}
// Brief delay between chunks
OSTimeDly(TICKS_PER_SECOND / 100);
}
ws_flush(ws_fd);
printf("Large payload sent successfully\n");
return true;
}
Broadcast to Multiple WebSockets
void BroadcastMessage(const char *message) {
int length = strlen(message);
int success_count = 0;
int fail_count = 0;
printf("Broadcasting message to all WebSockets: %s\n", message);
for (int i = 0; i < MAX_WEBSOCKETS; i++) {
WebSocket *ws = GetWebSocketByIndex(i);
if (ws != nullptr) {
int fd = ws->GetFd();
int bytes_written = ws_write(fd, message, length);
if (bytes_written == length) {
ws_flush(fd);
success_count++;
} else {
fail_count++;
printf("WARNING: Failed to send to WebSocket %d\n", i);
}
}
}
printf("Broadcast complete: %d success, %d failed\n",
success_count, fail_count);
}
Echo Server Pattern
void WebSocketEchoHandler(int ws_fd) {
char read_buffer[512];
while (1) {
int bytes_read = ws_read(ws_fd, read_buffer, sizeof(read_buffer) - 1);
if (bytes_read > 0) {
read_buffer[bytes_read] = '\0';
// Echo back what was received
int bytes_written = ws_write(ws_fd, read_buffer, bytes_read);
if (bytes_written > 0) {
ws_flush(ws_fd);
printf("Echoed %d bytes\n", bytes_written);
} else {
printf("Echo write failed\n");
break;
}
} else if (bytes_read < 0) {
printf("Connection closed\n");
break;
}
OSTimeDly(TICKS_PER_SECOND / 100);
}
}

◆ ws_write_notify()

static void NB::WebSocket::ws_write_notify ( int fd)
static

TCP write notification callback invoked when buffer space becomes available.

Description

This static callback function is invoked by the NetBurner TCP/IP stack when the TCP send buffer has space available for writing. It allows the WebSocket handler to resume sending data that was previously blocked due to a full send buffer. This implements flow control for outbound WebSocket traffic.

Parameters

Parameters
fdThe file descriptor of the TCP socket with available buffer space

Usage Notes

  • Automatically called by the network stack when send buffer space is available
  • Essential for handling EWOULDBLOCK conditions on non-blocking sockets
  • Should attempt to flush pending outbound data
  • Must not block or perform lengthy operations
  • Typically registered via SetSocketWriteNotify() or similar
  • May be called multiple times as buffer space becomes available

Flow Control

This callback enables proper flow control by:

  • Resuming writes after EWOULDBLOCK
  • Preventing busy-waiting on socket writability
  • Managing outbound message queues efficiently

Thread Safety

Called from the network stack context. Ensure thread-safe access to write queues.

See Also


Expand for Example Usage

Examples

Example 1: Basic Write Notification Setup
class WebSocketHandler {
private:
static void ws_write_notify(int fd) {
printf("Write space available on fd %d\r\n", fd);
// Attempt to flush pending data
if (HasPendingData(fd)) {
FlushPendingData(fd);
}
}
public:
void RegisterNotifications(int sockfd) {
// Register write callback with TCP stack
SetSocketWriteNotify(sockfd, ws_write_notify);
}
};
Example 2: Message Queue Flushing
class WebSocketConnection {
private:
static std::map<int, std::queue<WebSocketMessage>> writeQueues;
static void ws_write_notify(int fd) {
auto& queue = writeQueues[fd];
// Try to send queued messages
while (!queue.empty()) {
WebSocketMessage& msg = queue.front();
int bytesSent = write(fd, msg.data, msg.length);
if (bytesSent == msg.length) {
// Complete message sent
queue.pop();
}
else if (bytesSent > 0) {
// Partial send - update message
msg.data += bytesSent;
msg.length -= bytesSent;
break; // Wait for next notification
}
else {
// EWOULDBLOCK - buffer full again
if (errno == EWOULDBLOCK) break;
// Error - close connection
printf("Write error on fd=%d: %d\r\n", fd, errno);
CleanupConnection(fd);
break;
}
}
// Disable write notifications if queue empty
if (queue.empty()) {
SetSocketWriteNotify(fd, nullptr);
}
}
};
int write(int fd, const char *buf, int nbytes)
Write data to the stream associated with a file descriptor (fd). Can be used to write data to stdio,...
Example 3: Advanced Flow Control with Backpressure
static void ws_write_notify(int fd) {
ConnectionState* conn = GetConnection(fd);
if (!conn) return;
// Update connection state
conn->writeBlocked = false;
// Attempt to flush buffer
while (conn->sendBuffer.HasData()) {
const uint8_t* data = conn->sendBuffer.GetData();
size_t length = conn->sendBuffer.GetLength();
int bytesSent = write(fd, data, length);
if (bytesSent > 0) {
conn->sendBuffer.Consume(bytesSent);
conn->totalBytesSent += bytesSent;
if (bytesSent < (int)length) {
// Partial write - buffer full again
printf("Partial write: %d/%zu bytes\r\n", bytesSent, length);
conn->writeBlocked = true;
break;
}
}
else if (errno == EWOULDBLOCK) {
conn->writeBlocked = true;
break;
}
else {
// Error
printf("Write failed on fd=%d: errno=%d\r\n", fd, errno);
CloseConnection(fd);
return;
}
}
// Notify application that backpressure is relieved
if (!conn->writeBlocked && conn->onWriteReady) {
conn->onWriteReady(fd);
}
}
static void ws_write_notify(int fd)
TCP write notification callback invoked when buffer space becomes available.

Member Data Documentation

◆ m_fd_tcp

int NB::WebSocket::m_fd_tcp

TCP socket file descriptor (underlying transport)

This member variable stores the file descriptor for the underlying TCP socket that provides the transport layer for the WebSocket connection. The TCP socket handles the actual network communication, while the WebSocket protocol operates on top of it.

Key characteristics:

  • Created during initial connection establishment
  • Used for all low-level socket operations (send, recv, setsockopt, etc.)
  • Remains valid throughout the WebSocket connection lifetime
  • Closed when the WebSocket connection is terminated

The TCP socket is typically established through one of two methods:

  1. Client-initiated connection: Created via connect() to a remote server
  2. Server-accepted connection: Obtained from accept() on a listening socket

After the WebSocket handshake completes, this socket is "promoted" to WebSocket mode, but the underlying TCP file descriptor remains the same and continues to handle the transport.

Common operations using this file descriptor:

  • Reading raw data from the network
  • Writing framed WebSocket data to the network
  • Setting socket options (SO_KEEPALIVE, TCP_NODELAY, etc.)
  • Checking socket status and errors
  • Closing the connection
Note
This is the actual system socket file descriptor. Operations on this socket must be thread-safe when multiple tasks access the WebSocket.
A value of -1 or similar invalid descriptor indicates no connection.
Direct manipulation of this file descriptor should be avoided in favor of using WebSocket member functions, which handle protocol requirements.
See also
m_fd_ws for the application-level WebSocket identifier
socket_crit for thread-safe access to socket operations
Example Usage

Expand for Example Usage

Examples

Check if Socket is Valid
WebSocket *ws = GetActiveWebSocket();
if (ws->m_fd_tcp >= 0) {
printf("TCP socket is valid (fd: %d)\n", ws->m_fd_tcp);
} else {
printf("TCP socket is invalid\n");
}
Set Socket Options on TCP Layer
void ConfigureTcpSocket(WebSocket *ws) {
if (ws->m_fd_tcp < 0) {
printf("ERROR: Invalid TCP socket\n");
return;
}
// Set TCP_NODELAY for low latency
int flag = 1;
if (setsockopt(ws->m_fd_tcp, IPPROTO_TCP, TCP_NODELAY,
&flag, sizeof(flag)) == 0) {
printf("TCP_NODELAY set on fd %d\n", ws->m_fd_tcp);
}
// Enable keepalive
flag = 1;
if (setsockopt(ws->m_fd_tcp, SOL_SOCKET, SO_KEEPALIVE,
&flag, sizeof(flag)) == 0) {
printf("SO_KEEPALIVE set on fd %d\n", ws->m_fd_tcp);
}
}
Get Socket Information
void DisplaySocketInfo(WebSocket *ws) {
if (ws->m_fd_tcp < 0) {
printf("No TCP socket\n");
return;
}
// Get local address
IPADDR local_addr;
WORD local_port;
GetSocketAddr(ws->m_fd_tcp, &local_addr, &local_port);
// Get peer address
IPADDR peer_addr;
WORD peer_port;
GetSocketPeer(ws->m_fd_tcp, &peer_addr, &peer_port);
printf("TCP Socket Info (fd: %d):\n", ws->m_fd_tcp);
printf(" Local: %s:%d\n", IPAddressToString(local_addr), local_port);
printf(" Peer: %s:%d\n", IPAddressToString(peer_addr), peer_port);
}
Check Socket Error Status
bool CheckSocketError(WebSocket *ws) {
if (ws->m_fd_tcp < 0) {
return true; // Invalid socket is an error
}
int error = 0;
socklen_t len = sizeof(error);
if (getsockopt(ws->m_fd_tcp, SOL_SOCKET, SO_ERROR,
&error, &len) == 0) {
if (error != 0) {
printf("Socket error on fd %d: %d\n", ws->m_fd_tcp, error);
return true;
}
}
return false;
}
Monitor Multiple WebSocket TCP Sockets
void MonitorAllTcpSockets() {
printf("TCP Socket Status:\n");
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->GetState() != WS_STATE_CLOSED && ws->m_fd_tcp >= 0) {
printf(" [%d] TCP fd=%d, WS fd=%d, State=%d\n",
i, ws->m_fd_tcp, ws->m_fd_ws, ws->GetState());
}
}
}
Get Socket Buffer Sizes
void DisplayBufferSizes(WebSocket *ws) {
if (ws->m_fd_tcp < 0) {
printf("Invalid socket\n");
return;
}
int sndbuf, rcvbuf;
socklen_t len = sizeof(int);
getsockopt(ws->m_fd_tcp, SOL_SOCKET, SO_SNDBUF, &sndbuf, &len);
getsockopt(ws->m_fd_tcp, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &len);
printf("TCP Socket %d Buffer Sizes:\n", ws->m_fd_tcp);
printf(" Send buffer: %d bytes\n", sndbuf);
printf(" Recv buffer: %d bytes\n", rcvbuf);
}

◆ m_fd_ws

int NB::WebSocket::m_fd_ws

WebSocket file descriptor (application-level identifier)

This member variable stores an application-level file descriptor or identifier that represents this WebSocket connection. While m_fd_tcp represents the underlying TCP transport, m_fd_ws provides a WebSocket-specific identifier used by the WebSocket management system.

Key characteristics:

  • Assigned when a TCP connection is promoted to a WebSocket
  • Used to identify and look up WebSocket instances
  • May differ from m_fd_tcp in systems with separate numbering schemes
  • Used as a key in WebSocket registration/lookup operations

This identifier is used throughout the WebSocket API for operations such as:

  • Finding a WebSocket instance via GetWebSocketRecord()
  • Static wrapper functions (ws_read, ws_write, etc.)
  • Connection management and tracking
  • Diagnostic and monitoring operations

In some implementations, m_fd_ws may equal m_fd_tcp, indicating the same file descriptor is used for both layers. In other implementations, they may be different to provide abstraction between layers.

Note
This identifier is set during WebSocket promotion (promote_tcp_ws).
A value of -1 or similar invalid identifier indicates no WebSocket has been established.
Application code should primarily use this identifier when calling WebSocket-specific functions rather than accessing m_fd_tcp directly.
See also
m_fd_tcp for the underlying TCP socket file descriptor
GetWebSocketRecord() which uses this identifier to find WebSocket instances
Example Usage

Expand for Example Usage

Examples

Get WebSocket Identifier
WebSocket *ws = GetActiveWebSocket();
if (ws->m_fd_ws >= 0) {
printf("WebSocket identifier: %d\n", ws->m_fd_ws);
printf("TCP socket: %d\n", ws->m_fd_tcp);
} else {
printf("WebSocket not established\n");
}
Use WebSocket Identifier with Static Functions
void SendMessage(WebSocket *ws, const char *message) {
int ws_fd = ws->m_fd_ws;
if (ws_fd < 0) {
printf("ERROR: Invalid WebSocket identifier\n");
return;
}
// Use static wrapper functions with WebSocket identifier
int result = ws_write(ws_fd, message, strlen(message));
if (result > 0) {
ws_flush(ws_fd);
printf("Message sent via WebSocket %d\n", ws_fd);
}
}
Compare TCP and WebSocket Identifiers
void DisplayIdentifiers(WebSocket *ws) {
printf("Connection Identifiers:\n");
printf(" TCP fd: %d\n", ws->m_fd_tcp);
printf(" WebSocket fd: %d\n", ws->m_fd_ws);
if (ws->m_fd_tcp == ws->m_fd_ws) {
printf(" (Same identifier used for both layers)\n");
} else {
printf(" (Separate identifiers for layers)\n");
}
}
Lookup WebSocket by Identifier
WebSocket* FindWebSocket(int ws_fd) {
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->m_fd_ws == ws_fd) {
printf("Found WebSocket at index %d\n", i);
return ws;
}
}
printf("WebSocket %d not found\n", ws_fd);
return nullptr;
}
Track Active WebSocket Identifiers
void ListActiveWebSockets() {
printf("Active WebSocket Identifiers:\n");
int count = 0;
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->m_fd_ws >= 0 && ws->GetState() == WS_STATE_OPEN) {
printf(" [%d] WS fd=%d (TCP fd=%d)\n",
i, ws->m_fd_ws, ws->m_fd_tcp);
count++;
}
}
printf("Total active: %d\n", count);
}
Validate WebSocket Identifier
bool IsValidWebSocketId(int ws_fd) {
if (ws_fd < 0) {
return false;
}
// Check if this identifier belongs to an active WebSocket
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->m_fd_ws == ws_fd) {
return true;
}
}
return false;
}

◆ s_openSocketCount

int NB::WebSocket::s_openSocketCount
static

Number of currently open WebSocket connections.

This static member variable tracks the total number of active WebSocket connections currently managed by the WebSocket system. It is incremented when a new WebSocket connection is established and decremented when a connection is closed.

This counter is useful for:

  • Monitoring system resource usage
  • Enforcing connection limits
  • Diagnostics and debugging
  • Load balancing decisions

The value ranges from 0 to WS_MAX_SOCKS (the maximum number of concurrent WebSocket connections supported by the system).

Note
This is a static variable shared across all WebSocket instances. Access should be synchronized in multi-threaded environments.
See also
WS_MAX_SOCKS for the maximum connection limit
WSSockStructs for the array of WebSocket structures
Example Usage

Expand for Example Usage

Examples

Check Connection Count
void DisplayWebSocketStatus() {
int active_connections = WebSocket::s_openSocketCount;
printf("Active WebSocket connections: %d/%d\n",
active_connections, WS_MAX_SOCKS);
if (active_connections >= WS_MAX_SOCKS) {
printf("WARNING: Maximum WebSocket connections reached!\n");
}
}
static int s_openSocketCount
Number of currently open WebSocket connections.
Definition websockets.h:4020
Enforce Connection Limit
bool CanAcceptNewConnection() {
printf("Cannot accept new connection: limit reached (%d)\n",
return false;
}
printf("Can accept connection (%d/%d slots used)\n",
return true;
}
Resource Monitoring
void MonitorWebSocketResources() {
int available = WS_MAX_SOCKS - used;
float usage_percent = (used * 100.0f) / WS_MAX_SOCKS;
printf("WebSocket Resource Usage:\n");
printf(" Used: %d\n", used);
printf(" Available: %d\n", available);
printf(" Usage: %.1f%%\n", usage_percent);
if (usage_percent > 80.0f) {
printf(" WARNING: High resource usage!\n");
}
}
Connection Acceptance Logic
int HandleNewWebSocketRequest(int tcp_fd) {
// Check if we can accept more connections
printf("Rejecting connection: only 1 slot remaining (reserved)\n");
SendWebSocketError(tcp_fd, 503, "Service Unavailable");
close(tcp_fd);
return -1;
}
// Promote to WebSocket
if (promote_tcp_ws(tcp_fd) == 0) {
printf("Connection accepted (%d/%d)\n",
return 0;
}
return -1;
}
Periodic Status Reporting
void WebSocketStatusTask(void *pd) {
while (1) {
if (count > 0) {
printf("[%lu] Active WebSocket connections: %d\n",
Secs, count);
}
// Report every 60 seconds
OSTimeDly(TICKS_PER_SECOND * 60);
}
}

◆ socket_crit

OS_CRIT NB::WebSocket::socket_crit

Critical section for thread-safe socket operations.

This member variable provides a critical section (mutual exclusion) mechanism to ensure thread-safe access to the WebSocket's socket operations. In a multi-threaded RTOS environment, multiple tasks may attempt to read from, write to, or modify the same WebSocket connection simultaneously, which could lead to data corruption or protocol violations.

The critical section protects:

  • Socket read and write operations
  • Buffer access and manipulation
  • State changes and transitions
  • Socket option modifications
  • Connection establishment and closure

Usage pattern:

  1. Enter critical section before socket operations
  2. Perform socket operations
  3. Exit critical section after operations complete

NetBurner RTOS provides OS_CRIT primitives for critical sections:

  • USER_ENTER_CRITICAL(): Enter critical section (acquire lock)
  • USER_EXIT_CRITICAL(): Exit critical section (release lock)

These operations disable task switching temporarily to ensure atomic execution of protected code sections. The critical section should be held for the minimum time necessary to avoid blocking other tasks.

Note
Critical sections should be kept as short as possible to minimize impact on system responsiveness.
Failing to properly enter/exit critical sections can lead to race conditions, data corruption, or deadlocks.
The OS_CRIT type is typically a structure that tracks the critical section state and may include nesting counters.
Warning
Never return from a function without exiting a critical section you entered. Always use balanced enter/exit calls.
Avoid performing blocking operations (delays, waits) inside critical sections as this defeats their purpose and can cause system performance issues.
See also
m_fd_tcp for the socket being protected
m_fd_ws for the WebSocket identifier
Example Usage

Expand for Example Usage

Examples

Protected Write Operation
int SafeWrite(WebSocket *ws, const char *data, int length) {
USER_ENTER_CRITICAL(&ws->socket_crit);
// Protected: Write operation is atomic
int bytes_written = write(ws->m_fd_tcp, data, length);
USER_EXIT_CRITICAL(&ws->socket_crit);
return bytes_written;
}
Protected Read Operation
int SafeRead(WebSocket *ws, char *buffer, int size) {
USER_ENTER_CRITICAL(&ws->socket_crit);
// Protected: Read operation is atomic
int bytes_read = read(ws->m_fd_tcp, buffer, size);
USER_EXIT_CRITICAL(&ws->socket_crit);
return bytes_read;
}
Protected State Change
void SetConnectionState(WebSocket *ws, WS_State new_state) {
USER_ENTER_CRITICAL(&ws->socket_crit);
// Protected: State change is atomic
WS_State old_state = ws->m_state;
ws->m_state = new_state;
USER_EXIT_CRITICAL(&ws->socket_crit);
printf("State changed: %d -> %d\n", old_state, new_state);
}
Protected Buffer Access
int GetBufferStats(WebSocket *ws, int *bytes_available, int *space_free) {
USER_ENTER_CRITICAL(&ws->socket_crit);
// Protected: Buffer state reads are consistent
*bytes_available = ws->rxBuffer.BytesAvail();
*space_free = ws->txBuffer.SpaceAvail();
USER_EXIT_CRITICAL(&ws->socket_crit);
return 0;
}
Error Handling in Critical Section
int SafeOperation(WebSocket *ws, const char *data, int length) {
USER_ENTER_CRITICAL(&ws->socket_crit);
int result = -1;
if (ws->m_fd_tcp >= 0) {
result = write(ws->m_fd_tcp, data, length);
if (result < 0) {
// Handle error inside critical section
ws->m_state = WS_STATE_ERROR;
}
}
USER_EXIT_CRITICAL(&ws->socket_crit);
return result;
}
Nested Critical Section (if supported)
void ComplexOperation(WebSocket *ws) {
USER_ENTER_CRITICAL(&ws->socket_crit);
// First operation
UpdateInternalState(ws);
// Nested critical section (if OS_CRIT supports nesting)
USER_ENTER_CRITICAL(&ws->socket_crit);
UpdateSocketOptions(ws);
USER_EXIT_CRITICAL(&ws->socket_crit);
// Second operation
FlushBuffers(ws);
USER_EXIT_CRITICAL(&ws->socket_crit);
}
Multiple Task Access Pattern
// Task 1: Writing to WebSocket
void WriterTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
while (1) {
char data[64];
GenerateData(data, sizeof(data));
USER_ENTER_CRITICAL(&ws->socket_crit);
ws->WriteData(data, sizeof(data));
USER_EXIT_CRITICAL(&ws->socket_crit);
OSTimeDly(TICKS_PER_SECOND);
}
}
// Task 2: Reading from WebSocket
void ReaderTask(void *pd) {
WebSocket *ws = (WebSocket *)pd;
while (1) {
char buffer[256];
USER_ENTER_CRITICAL(&ws->socket_crit);
int bytes = ws->ReadData(buffer, sizeof(buffer));
USER_EXIT_CRITICAL(&ws->socket_crit);
if (bytes > 0) {
ProcessData(buffer, bytes);
}
OSTimeDly(TICKS_PER_SECOND / 10);
}
}
Critical Section Timing Measurement
void MeasureCriticalSectionTime(WebSocket *ws) {
uint32_t start_tick = TimeTick;
USER_ENTER_CRITICAL(&ws->socket_crit);
// Perform operation
PerformSocketOperation(ws);
uint32_t end_tick = TimeTick;
USER_EXIT_CRITICAL(&ws->socket_crit);
uint32_t duration_ticks = end_tick - start_tick;
float duration_us = (duration_ticks * 1000000.0f) / TICKS_PER_SECOND;
printf("Critical section held for %.2f microseconds\n", duration_us);
if (duration_us > 1000.0f) {
printf("WARNING: Critical section held too long!\n");
}
}
Conditional Critical Section
void ConditionalProtectedOperation(WebSocket *ws, bool needs_protection) {
if (needs_protection) {
USER_ENTER_CRITICAL(&ws->socket_crit);
}
// Operation that may or may not need protection
PerformOperation(ws);
if (needs_protection) {
USER_EXIT_CRITICAL(&ws->socket_crit);
}
}
Diagnostic: Check Critical Section Status
void DiagnoseCriticalSection(WebSocket *ws) {
// Note: This is conceptual - actual implementation depends on OS_CRIT structure
printf("Critical Section Diagnostics:\n");
printf(" Address: %p\n", &ws->socket_crit);
// If OS_CRIT has a lock counter or owner field:
// printf(" Lock count: %d\n", ws->socket_crit.lock_count);
// printf(" Owner task: %d\n", ws->socket_crit.owner_task);
}

◆ WSSockStructs

WebSocket NB::WebSocket::WSSockStructs[WS_MAX_SOCKS]
static

Static array of WebSocket connection structures.

This static array holds all WebSocket connection structures, providing storage for up to WS_MAX_SOCKS concurrent WebSocket connections. Each element represents a potential WebSocket connection slot.

The array is pre-allocated at compile time, eliminating the need for dynamic memory allocation during runtime. This approach:

  • Provides predictable memory usage
  • Eliminates allocation failures
  • Improves performance by avoiding heap operations
  • Simplifies resource management

When a new WebSocket connection is established, an unused slot in this array is assigned. When a connection closes, its slot becomes available for reuse.

Note
This is a static array shared across all WebSocket operations. Individual elements are accessed via helper functions like GetWebSocketRecord().
See also
WS_MAX_SOCKS for the array size
s_openSocketCount for tracking active connections
GetWebSocketRecord() for finding WebSocket by file descriptor
Example Usage

Expand for Example Usage

Examples

Iterate All WebSocket Slots
void DisplayAllWebSockets() {
printf("WebSocket Connection List:\n");
printf("===========================\n");
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->IsActive()) {
printf("[%d] fd=%d, state=%d\n",
i, ws->GetFd(), ws->GetState());
}
}
}
Find Available Slot
WebSocket* FindAvailableWebSocketSlot() {
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (!ws->IsActive()) {
printf("Found available slot: %d\n", i);
return ws;
}
}
printf("No available WebSocket slots\n");
return nullptr;
}
Close All Active Connections
void CloseAllWebSockets() {
int closed_count = 0;
printf("Closing all WebSocket connections...\n");
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->IsActive()) {
int fd = ws->GetFd();
closed_count++;
printf(" Closed slot %d (fd %d)\n", i, fd);
}
}
printf("Closed %d WebSocket connections\n", closed_count);
}
Collect Connection Statistics
void CollectWebSocketStatistics() {
int active = 0;
int inactive = 0;
uint32_t total_bytes_sent = 0;
uint32_t total_bytes_received = 0;
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->IsActive()) {
active++;
total_bytes_sent += ws->GetBytesSent();
total_bytes_received += ws->GetBytesReceived();
} else {
inactive++;
}
}
printf("WebSocket Statistics:\n");
printf(" Active connections: %d\n", active);
printf(" Inactive slots: %d\n", inactive);
printf(" Total bytes sent: %lu\n", total_bytes_sent);
printf(" Total bytes received: %lu\n", total_bytes_received);
}
Broadcast to All Active Connections
void BroadcastToAllWebSockets(const char *message) {
int length = strlen(message);
int sent_count = 0;
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->IsActive()) {
int fd = ws->GetFd();
int bytes_written = ws_write(fd, message, length);
if (bytes_written > 0) {
ws_flush(fd);
sent_count++;
}
}
}
printf("Broadcast sent to %d WebSocket(s)\n", sent_count);
}
Find Idle Connections
void CloseIdleConnections(uint32_t max_idle_seconds) {
uint32_t current_time = Secs;
for (int i = 0; i < WS_MAX_SOCKS; i++) {
if (ws->IsActive()) {
uint32_t idle_time = current_time - ws->GetLastActivityTime();
if (idle_time > max_idle_seconds) {
int fd = ws->GetFd();
printf("Closing idle connection slot %d (idle %lu sec)\n",
i, idle_time);
}
}
}
}


The documentation for this class was generated from the following file: