WebSocket implementation to translate this sentence into English
Introduction to WebSocket
WebSocket is a protocol for full-duplex communication over a single TCP connection. It allows the server to actively push data to the client, solving the problem of server-initiated push that the HTTP protocol cannot achieve. The WebSocket protocol was standardized by IETF as RFC 6455 in 2011 and later by W3C.
WebSocket vs. HTTP
The HTTP protocol is stateless, requiring a new connection for each request, whereas WebSocket only needs to establish a connection once:
- HTTP is half-duplex, while WebSocket is full-duplex
- Each HTTP request includes header information, whereas WebSocket has minimal data transmission after connection establishment
- HTTP requires client polling to fetch new data, while WebSocket supports server-initiated push
// HTTP polling example
setInterval(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => console.log(data))
}, 1000);
// WebSocket example
const ws = new WebSocket('ws://example.com');
ws.onmessage = (event) => {
console.log(event.data);
};
WebSocket Implementation in Node.js
Using the ws Module
ws is the most popular WebSocket library in Node.js, known for its lightweight and efficient design:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log('New client connected');
ws.on('message', (message) => {
console.log(`Received message: ${message}`);
// Broadcast to all clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
});
ws.send('Welcome to the WebSocket server');
});
Using Socket.io
Socket.io offers advanced features like automatic reconnection and room support:
const server = require('http').createServer();
const io = require('socket.io')(server);
io.on('connection', (socket) => {
console.log('User connected');
socket.on('chat message', (msg) => {
io.emit('chat message', msg);
});
socket.on('disconnect', () => {
console.log('User disconnected');
});
});
server.listen(3000);
WebSocket Handshake Process
WebSocket connections are established via an HTTP upgrade request:
- Client sends a handshake request:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
- Server responds:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Message Frame Format
WebSocket data is transmitted in frames, which include:
- FIN: Indicates if this is the final frame
- Opcode: Operation code (0x1 for text, 0x2 for binary)
- Mask: Whether masking is applied
- Payload length: Data length
- Masking-key: Masking key (if present)
- Payload data: Actual data
// Manual WebSocket frame parsing (simplified)
function parseFrame(buffer) {
const firstByte = buffer.readUInt8(0);
const secondByte = buffer.readUInt8(1);
const opcode = firstByte & 0x0F;
const isMasked = Boolean((secondByte >>> 7) & 0x01);
let payloadLength = secondByte & 0x7F;
let offset = 2;
if (payloadLength === 126) {
payloadLength = buffer.readUInt16BE(offset);
offset += 2;
} else if (payloadLength === 127) {
payloadLength = buffer.readBigUInt64BE(offset);
offset += 8;
}
// Mask handling omitted...
return {
opcode,
payloadLength,
payloadData: buffer.slice(offset)
};
}
Heartbeat Mechanism
Implementation of a heartbeat mechanism to keep connections alive:
// Server-side heartbeat
function setupHeartbeat(ws) {
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
const interval = setInterval(() => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping(null, false);
}, 30000);
ws.on('close', () => clearInterval(interval));
}
// Client-side heartbeat
const ws = new WebSocket('ws://example.com');
const heartbeatInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.ping();
}
}, 25000);
ws.on('close', () => clearInterval(heartbeatInterval));
Security Considerations
Authentication and Authorization
// Token authentication example
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (request, socket, head) => {
const token = request.url.split('token=')[1];
if (!validateToken(token)) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request);
});
});
Message Validation
ws.on('message', (message) => {
try {
const data = JSON.parse(message);
if (!isValid(data)) {
throw new Error('Invalid message');
}
// Process valid message
} catch (err) {
ws.close(1008, 'Invalid message format');
}
});
Performance Optimization
Message Compression
const WebSocket = require('ws');
const zlib = require('zlib');
const wss = new WebSocket.Server({
port: 8080,
perMessageDeflate: {
zlibDeflateOptions: {
chunkSize: 1024,
memLevel: 7,
level: 3
},
zlibInflateOptions: {
chunkSize: 10 * 1024
},
threshold: 1024 // Only compress messages larger than this
}
});
Connection Limiting
const wss = new WebSocket.Server({ port: 8080 });
const connections = new Set();
wss.on('connection', (ws) => {
if (connections.size >= 1000) {
ws.close(1013, 'Server too busy');
return;
}
connections.add(ws);
ws.on('close', () => connections.delete(ws));
});
Practical Application Scenarios
Real-time Chat Application
// Server
wss.on('connection', (ws) => {
ws.userData = { id: generateId() };
ws.on('message', (message) => {
const data = JSON.parse(message);
switch(data.type) {
case 'join':
ws.userData.room = data.room;
break;
case 'message':
broadcastToRoom(ws.userData.room, {
type: 'message',
user: ws.userData.id,
text: data.text,
timestamp: Date.now()
});
break;
}
});
});
function broadcastToRoom(room, message) {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN &&
client.userData.room === room) {
client.send(JSON.stringify(message));
}
});
}
Real-time Data Monitoring
// Server pushing sensor data
const sensorData = {
temperature: 0,
humidity: 0
};
setInterval(() => {
// Simulate data changes
sensorData.temperature = 20 + 5 * Math.random();
sensorData.humidity = 50 + 10 * Math.random();
sensorData.timestamp = Date.now();
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
type: 'sensorUpdate',
data: sensorData
}));
}
});
}, 1000);
Error Handling and Debugging
Common Error Codes
- 1006: Abnormal closure
- 1001: Endpoint going away
- 1002: Protocol error
- 1008: Policy violation
- 1011: Server error
ws.on('close', (code, reason) => {
console.log(`Connection closed, code: ${code}, reason: ${reason}`);
if (code === 1006) {
console.log('Possible network issue causing abnormal disconnection');
}
});
ws.on('error', (error) => {
console.error('WebSocket error:', error);
});
Debugging Tools
Using browser developer tools to inspect WebSocket traffic:
- Chrome DevTools → Network → WS
- Click WebSocket connection to view message frames
- Filter and inspect each message's content
Browser Compatibility
Modern browsers support the WebSocket API, but fallback solutions should be considered:
function connectWebSocket() {
if ('WebSocket' in window) {
return new WebSocket('ws://example.com');
} else if ('MozWebSocket' in window) {
return new MozWebSocket('ws://example.com');
} else {
// Fallback to long polling
setupPolling();
return null;
}
}
function setupPolling() {
// Implement HTTP long polling
let timestamp = 0;
function poll() {
fetch(`/poll?since=${timestamp}`)
.then(res => res.json())
.then(data => {
timestamp = data.timestamp;
processMessages(data.messages);
poll();
});
}
poll();
}
WebSocket Extensions
Custom Protocols
// Define a simple protocol
// Format: type|payload
const PROTOCOL = {
MESSAGE: 'msg',
NOTIFICATION: 'noti',
COMMAND: 'cmd'
};
// Send message
function sendMessage(ws, type, payload) {
ws.send(`${type}|${JSON.stringify(payload)}`);
}
// Receive and process
ws.on('message', (data) => {
const [type, payload] = data.split('|');
const parsedPayload = JSON.parse(payload);
switch(type) {
case PROTOCOL.MESSAGE:
handleMessage(parsedPayload);
break;
case PROTOCOL.NOTIFICATION:
handleNotification(parsedPayload);
break;
}
});
Binary Data Transmission
// Send binary data
const buffer = new ArrayBuffer(8);
const view = new Uint8Array(buffer);
for (let i = 0; i < 8; i++) view[i] = i;
ws.onopen = () => {
ws.send(buffer);
};
// Receive binary data
ws.onmessage = (event) => {
if (event.data instanceof ArrayBuffer) {
const view = new Uint8Array(event.data);
console.log('Received binary data:', view);
}
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn