|
WebSocket provides full-duplex communication over a single TCP connection. Node.js 22+ includes a built-in WebSocket client (the server still requires a library).
|
|
|
Connecting to a WebSocket Server
Create a WebSocket connection to a server endpoint.
|
const ws = new WebSocket('wss://echo.websocket.org');
|
|
Connection Events
WebSocket emits events for connection lifecycle.
|
ws.addEventListener('open', () => {
console.log('Connected to server');
ws.send('Hello, WebSocket!');
});
ws.addEventListener('close', (event) => {
console.log('Disconnected:', event.code, event.reason);
});
ws.addEventListener('error', (error) => {
console.error('WebSocket error:', error.message);
});
|
|
Receiving Messages
Handle incoming messages with the 'message' event.
|
ws.addEventListener('message', (event) => {
console.log('Received:', event.data);
});
|
|
Sending Messages
Send strings or binary data (ArrayBuffer, Blob) to the server.
|
function sendMessages(socket) {
socket.send('Hello, server!');
socket.send(JSON.stringify({ type: 'greeting', message: 'Hi!' }));
const buffer = new Uint8Array([1, 2, 3, 4, 5]);
socket.send(buffer);
}
|
|
Connection State
Check the readyState property to know the connection status.
|
function checkState(socket) {
switch (socket.readyState) {
case WebSocket.CONNECTING:
console.log('Connecting...');
break;
case WebSocket.OPEN:
console.log('Connected');
break;
case WebSocket.CLOSING:
console.log('Closing...');
break;
case WebSocket.CLOSED:
console.log('Closed');
break;
}
}
|
|
Closing the Connection
Close the connection gracefully with optional code and reason.
|
function closeConnection(socket) {
socket.close();
socket.close(1000, 'Normal closure');
}
|
|
Buffered Data
Check how much data is queued for sending.
|
function checkBuffer(socket) {
console.log('Buffered bytes:', socket.bufferedAmount);
if (socket.bufferedAmount === 0) {
socket.send('More data');
}
}
|
|
Binary Data Handling
Configure how binary data is received.
Handle binary messages
|
function configureBinary(socket) {
socket.binaryType = 'arraybuffer';
socket.binaryType = 'blob';
}
function handleBinary(event) {
if (event.data instanceof ArrayBuffer) {
const view = new Uint8Array(event.data);
console.log('Binary data:', view);
}
}
|
|
Reconnection Logic
Implement automatic reconnection when connection drops.
|
function createReconnectingSocket(url, options = {}) {
const { maxRetries = 5, baseDelay = 1000 } = options;
let retries = 0;
let socket = null;
function connect() {
socket = new WebSocket(url);
socket.addEventListener('open', () => {
console.log('Connected');
retries = 0;
});
socket.addEventListener('close', (event) => {
if (event.code !== 1000 && retries < maxRetries) {
const delay = baseDelay * Math.pow(2, retries);
console.log(`Reconnecting in ${delay}ms...`);
setTimeout(connect, delay);
retries++;
}
});
socket.addEventListener('error', () => {
});
return socket;
}
return connect();
}
|
|
Heartbeat/Ping-Pong
Keep connection alive with periodic pings.
|
function setupHeartbeat(socket) {
const HEARTBEAT_INTERVAL = 30000;
const heartbeat = setInterval(() => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify({ type: 'ping' }));
}
}, HEARTBEAT_INTERVAL);
socket.addEventListener('close', () => {
clearInterval(heartbeat);
});
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
if (data.type === 'pong') {
console.log('Heartbeat acknowledged');
}
});
}
|
|
Message Queue
Queue messages when connection is not ready.
|
class WebSocketWithQueue {
constructor(url) {
this.url = url;
this.queue = [];
this.connect();
}
connect() {
this.socket = new WebSocket(this.url);
this.socket.addEventListener('open', () => {
while (this.queue.length > 0) {
const message = this.queue.shift();
this.socket.send(message);
}
});
}
send(message) {
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(message);
} else {
this.queue.push(message);
}
}
}
|
|
Practical Example: Chat Client
A simple chat client implementation.
Usage example (won't run without a real server)
|
class ChatClient {
constructor(serverUrl) {
this.serverUrl = serverUrl;
this.handlers = new Map();
}
connect() {
return new Promise((resolve, reject) => {
this.socket = new WebSocket(this.serverUrl);
this.socket.addEventListener('open', () => {
console.log('Chat connected');
resolve();
});
this.socket.addEventListener('error', reject);
this.socket.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
const handler = this.handlers.get(message.type);
if (handler) handler(message);
});
});
}
on(type, handler) {
this.handlers.set(type, handler);
}
sendMessage(text) {
this.socket.send(JSON.stringify({
type: 'message',
text,
timestamp: Date.now()
}));
}
joinRoom(room) {
this.socket.send(JSON.stringify({
type: 'join',
room
}));
}
disconnect() {
this.socket.close(1000, 'User disconnected');
}
}
async function chatExample() {
const chat = new ChatClient('wss://chat.example.com');
chat.on('message', (msg) => {
console.log(`[${msg.user}]: ${msg.text}`);
});
chat.on('userJoined', (msg) => {
console.log(`${msg.user} joined the room`);
});
await chat.connect();
chat.joinRoom('general');
chat.sendMessage('Hello everyone!');
}
|