阿里云主机折上折
  • 微信号
Current Site:Index > WebSocket implementation to translate this sentence into English

WebSocket implementation to translate this sentence into English

Author:Chuan Chen 阅读数:7592人阅读 分类: Node.js

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:

  1. 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
  1. 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:

  1. Chrome DevTools → Network → WS
  2. Click WebSocket connection to view message frames
  3. 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

上一篇:TCP/UDP编程

下一篇:网络代理实现

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.