WebSocket Basic Usage
Constructor
The WebSocket constructor is used to create a new WebSocket instance, serving as the entry point for establishing a WebSocket connection with a server. The constructor accepts two parameters:
var socket = new WebSocket(url, [protocols]);- url: Required parameter, specifies the URL of the WebSocket server. The protocol prefix should be
ws://(unencrypted) orwss://(encrypted). - protocols: Optional parameter, a string or array of strings specifying acceptable subprotocols. The server can select one protocol to respond with, or if none are provided or accepted, the connection may fail or default to no-protocol mode.
Instance Properties
WebSocket instances provide several properties to check the connection status and configuration:
- readyState: Read-only property, returns the connection state, with values 0 (CONNECTING), 1 (OPEN), 2 (CLOSING), or 3 (CLOSED).
- bufferedAmount: Read-only property, indicates the number of bytes of UTF-8 text or binary data that have not yet been sent to the network.
- extensions: Read-only property, returns the extensions used by the connection, or an empty string if none are specified or used.
- protocol: Read-only property, returns the selected subprotocol if the connection is successful and a protocol was specified; otherwise, an empty string.
Code Example:
// Create WebSocket instance
var socket = new WebSocket('ws://your-websocket-server.com');
// Listen for connection open event
socket.addEventListener('open', function(event) {
console.log('WebSocket connected!');
// Check connection state using readyState property
console.log('Current readyState:', socket.readyState); // Should output 1, indicating connection is open
// Check buffer size before sending large data
var largeData = new Array(100000).fill('a').join('');
console.log('BufferedAmount before sending:', socket.bufferedAmount);
socket.send(largeData);
console.log('BufferedAmount after sending:', socket.bufferedAmount);
// View negotiated subprotocol
console.log('Negotiated protocol:', socket.protocol);
});
// Listen for close event, check final readyState
socket.addEventListener('close', function(event) {
console.log('Connection closed. Final readyState:', socket.readyState); // Should output 3, indicating connection is closed
});Instance Methods
WebSocket instances provide several core methods to control the connection and data transfer:
- send(data): Sends data to the server.
datacan be a string,ArrayBuffer, orBlobobject. WebSocket automatically sets the appropriate opcode based on the data type. - close([code[, reason]]): Closes the WebSocket connection.
codeis an optional close status code (typically 1000 for normal closure), andreasonis an optional human-readable close reason string. - addEventListener(event, listener): Adds an event listener to the WebSocket instance for events such as
open,message,error, andclose.
Code Example:
var socket = new WebSocket('ws://your-websocket-server.com');
// Dynamically add open event listener
socket.addEventListener('open', function(event) {
console.log('Connection opened!');
socket.send('Initial message from client.');
});
// Example of sending binary data
function sendBinaryData() {
var binaryData = new Uint8Array([0, 255, 127, 64]);
socket.send(binaryData);
console.log('Binary data sent.');
}
// Add custom close logic
function customClose() {
socket.close(1001, 'User requested close.'); // 1001 is a recommended status code for normal closure
}
// Dynamically add close event listener
socket.addEventListener('close', function(event) {
console.log('Connection closed with code:', event.code, 'and reason:', event.reason);
});
// Call sendBinaryData or customClose at some point, e.g., when a user clicks a button
document.getElementById('sendBinaryBtn').addEventListener('click', sendBinaryData);
document.getElementById('closeBtn').addEventListener('click', customClose);Event Handling
WebSocket instances provide several properties and events to track connection status and handle data transfer.
onopen: Event handler triggered when the connection is established.
socket.onopen = function(event) {
console.log('Connection opened!');
};onmessage: Triggered when a message is received from the server, with event.data containing the message content.
socket.onmessage = function(event) {
console.log('Received:', event.data);
};onerror: Triggered when an error occurs.
socket.onerror = function(error) {
console.error('Error occurred:', error);
};onclose: Triggered when the connection is closed, with event.code and event.reason providing closure details.
socket.onclose = function(event) {
if (event.wasClean) {
console.log(`[close] Clean close, code=${event.code} reason=${event.reason}`);
} else {
console.log('[close] Connection died');
}
};Inheritance
The EventTarget interface is implemented by objects that can receive events and create listeners. In other words, any event target implements the three methods associated with this interface.
Element and its children, document, and window are the most common event targets, but other objects can also be event targets, such as XMLHttpRequest, AudioNode, and AudioContext.
EventTarget.addEventListener()
Registers an event handler for a specific event type on the EventTarget.EventTarget.removeEventListener()
Removes an event listener from the EventTarget.EventTarget.dispatchEvent()
Dispatches an event to this EventTarget.
Sending and Receiving Data
WebSocket instances provide the send() method to send data to the server, which can be text or binary data.
socket.send('Hello Server!');
socket.send(new Uint8Array());Complete WebSocket Class Encapsulation
class CustomWebSocket extends WebSocket {
constructor(url, protocols) {
super(url, protocols);
this.reconnectInterval = 5000; // Reconnect interval in milliseconds
this.isReconnecting = false;
this.addEventListener('close', this.handleClose.bind(this));
}
handleClose(event) {
if (!this.isReconnecting && (event.code !== 1000)) { // 1000 code means normal closure, no reconnect attempt
console.log('Connection closed unexpectedly. Reconnecting in', this.reconnectInterval/1000, 'seconds...');
this.isReconnecting = true;
setTimeout(() => {
this.connect();
this.isReconnecting = false;
}, this.reconnectInterval);
}
}
connect() {
super.open(); // WebSocket's open method is internal; this simulates reconnect logic
console.log('Reconnected to WebSocket server.');
}
sendMessage(message) {
if (this.readyState === WebSocket.OPEN) {
super.send(message);
console.log('Sent:', message);
} else {
console.warn('WebSocket is not open, message not sent:', message);
}
}
onMessage(callback) {
this.addEventListener('message', function(event) {
callback(event.data);
});
}
}Next, we use the CustomWebSocket class defined above to create a WebSocket connection and demonstrate how to send and receive data.
// Instantiate custom WebSocket
const ws = new CustomWebSocket('ws://your-websocket-server.com');
// Set message reception handler
ws.onMessage((data) => {
console.log('Received:', data);
});
// Send a message
ws.sendMessage('Hello, WebSocket Server!');
// Close the connection at some point
// ws.close(1000, 'User logout');- Inheritance and Extension: By extending
WebSocket, we created aCustomWebSocketclass that inherits from the native WebSocket class, allowing custom logic like automatic reconnection. - Sending Data: The
sendMessagemethod encapsulates sending logic, adding a connection state check to ensure data is only sent when the connection is open. - Receiving Data: The
onMessagemethod provides a user-friendly interface to register callbacks for message reception events, simplifying message handling. - Error Handling and Reconnection: In the
handleClosemethod, we implemented basic error handling and reconnection logic to attempt re-establishing the connection when it closes unexpectedly.
Client API
WebSocket Object Creation (new WebSocket(url))
WebSocket Client Creation Process:
// Basic creation
const socket = new WebSocket('ws://example.com/socket');
// Creation with protocol versions
const socketWithProtocol = new WebSocket('ws://example.com/socket', ['v1.protocol', 'v2.protocol']);
// Creation with custom headers (Note: Custom headers cannot be set directly in browser environments)
// Requires extensions or proxy serversUnderlying Implementation Principle:
// Pseudocode showing internal logic of WebSocket constructor
function WebSocket(url, protocols) {
// 1. Parse URL
const parsedUrl = parseUrl(url);
// 2. Validate protocols
if (protocols) {
validateProtocols(protocols);
}
// 3. Create underlying connection
this._connection = new UnderlyingConnection(parsedUrl, protocols);
// 4. Initialize event system
this._events = {
open: new EventTarget(),
message: new EventTarget(),
error: new EventTarget(),
close: new EventTarget()
};
// 5. Start handshake process
this._startHandshake();
}Event Listening (onopen, onmessage, onerror, onclose)
Complete Event Listening Example:
const socket = new WebSocket('ws://example.com/socket');
// Connection open event
socket.onopen = function(event) {
console.log('WebSocket connection established');
// Send initial message here
socket.send('Hello Server!');
};
// Message reception event
socket.onmessage = function(event) {
if (event.data instanceof Blob) {
// Handle binary data
processBinaryData(event.data);
} else {
// Handle text data
console.log('Received message:', event.data);
}
};
// Error handling event
socket.onerror = function(error) {
console.error('WebSocket error:', error);
// Implement reconnection logic
scheduleReconnect();
};
// Connection close event
socket.onclose = function(event) {
console.log('WebSocket connection closed');
console.log('Close code:', event.code);
console.log('Close reason:', event.reason);
// Decide whether to reconnect based on close code
if (shouldReconnect(event.code)) {
scheduleReconnect();
}
};Event Types Details:
| Event Type | Trigger Timing | Event Object Properties |
|---|---|---|
| open | Connection successfully established | – |
| message | Message received | data (message content), origin (source), lastEventId (last event ID) |
| error | Error occurs | error (error object) |
| close | Connection closed | code (close code), reason (close reason), wasClean (whether closed cleanly) |
Message Sending (send() Method)
Message Sending Method Details:
// Send text message
socket.send('Hello, WebSocket!');
// Send JSON data
const data = { type: 'message', content: 'Hello' };
socket.send(JSON.stringify(data));
// Send binary data (Blob)
const blob = new Blob(['binary data'], { type: 'application/octet-stream' });
socket.send(blob);
// Send binary data (ArrayBuffer)
const buffer = new ArrayBuffer(128);
socket.send(buffer);
// Send binary data (Uint8Array)
const uint8Array = new Uint8Array([1, 2, 3, 4]);
socket.send(uint8Array);Underlying Sending Process:
// Pseudocode showing internal logic of send() method
WebSocket.prototype.send = function(data) {
// 1. Check connection state
if (this.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket is not open');
}
// 2. Create WebSocket frame based on data type
let frame;
if (typeof data === 'string') {
// Text frame
frame = createTextFrame(data);
} else if (data instanceof Blob) {
// Binary frame (Blob)
frame = createBinaryFrame(data);
} else if (data instanceof ArrayBuffer) {
// Binary frame (ArrayBuffer)
frame = createBinaryFrame(new Uint8Array(data));
} else if (data instanceof ArrayBufferView) {
// Binary frame (ArrayBufferView)
frame = createBinaryFrame(data);
} else {
throw new Error('Unsupported data type');
}
// 3. Add frame to send queue
this._sendQueue.push(frame);
// 4. Start sending if no data is currently being sent
if (!this._isSending) {
this._processSendQueue();
}
};Connection Closure (close() Method)
Connection Closure Method Details:
// Normal connection closure (code 1000 indicates normal closure)
socket.close(1000, 'Normal closure');
// Abnormal closure (code 1001 indicates endpoint going away)
socket.close(1001, 'Endpoint going away');
// No code or reason specified (defaults to 1005)
socket.close();Close Code Standards:
| Code | Meaning |
|---|---|
| 1000 | Normal closure |
| 1001 | Endpoint going away |
| 1002 | Protocol error |
| 1003 | Received unacceptable data |
| 1005 | No status code (reserved) |
| 1006 | Connection closed abnormally |
| 1011 | Server error |
Underlying Closure Process:
// Pseudocode showing internal logic of close() method
WebSocket.prototype.close = function(code, reason) {
// 1. Validate parameters
if (code && (code < 1000 || code > 4999)) {
throw new Error('Invalid close code');
}
// 2. Return if connection is already closing or closed
if (this.readyState === WebSocket.CLOSING || this.readyState === WebSocket.CLOSED) {
return;
}
// 3. Set connection state to CLOSING
this.readyState = WebSocket.CLOSING;
// 4. Create close frame
const closeFrame = createCloseFrame(code || 1005, reason || '');
// 5. Send close frame
this._sendFrame(closeFrame);
// 6. Start close timeout timer (if no peer close confirmation received)
this._closeTimeout = setTimeout(() => {
if (this.readyState === WebSocket.CLOSING) {
this._forceClose();
}
}, this._closeTimeoutDuration);
};
// Force close connection (when no peer response received)
WebSocket.prototype._forceClose = function() {
this.readyState = WebSocket.CLOSED;
this._connection.close();
this._events.close.dispatchEvent(new CloseEvent('close', {
code: 1006,
reason: 'Connection was closed abnormally',
wasClean: false
}));
};Error Handling and Reconnection Mechanism
Error Handling Best Practices:
// 1. Basic error handling
socket.onerror = function(error) {
console.error('WebSocket error:', error);
};
// 2. Reconnection mechanism implementation
class WebSocketClient {
constructor(url) {
this.url = url;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000; // Initial reconnect delay of 1 second
this.socket = null;
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.onopen = () => {
console.log('WebSocket connection established');
this.reconnectAttempts = 0; // Reset reconnect counter
};
this.socket.onclose = (event) => {
console.log('WebSocket connection closed');
if (this.shouldReconnect(event.code)) {
this.scheduleReconnect();
}
};
this.socket.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
shouldReconnect(code) {
// Reconnect only for non-normal closure or network errors
return code !== 1000; // 1000 indicates normal closure
}
scheduleReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.log('Maximum reconnect attempts reached');
return;
}
// Exponential backoff algorithm
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectAttempts),
30000 // Maximum delay of 30 seconds
);
this.reconnectAttempts++;
console.log(`Will attempt to reconnect in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
setTimeout(() => {
this.connect();
}, delay);
}
}
// Usage example
const client = new WebSocketClient('ws://example.com/socket');Advanced Reconnection Strategies:
- Exponential Backoff Algorithm: Double the delay time for each reconnect attempt until the maximum delay is reached.
- Network Status Detection: Check network connectivity before attempting to reconnect.
- Heartbeat Detection: Periodically send Ping frames to check connection health.
- Multi-level Reconnection: Adopt different reconnection strategies based on error types.



