掌握WebSocket子协议传递,提升Web应用实时交互能力

183 阅读3分钟

在现代Web开发中,实时通信已经成为不可或缺的一部分。无论是聊天应用、股票交易界面还是协同编辑工具,实时更新都是提升用户体验的关键因素之一。WebSocket作为HTML5的一个重要特性,提供了全双工的网络通信功能,使得服务器和客户端可以实时交换数据。而今天我们要深入探讨的是WebSocket中的一个高级话题——子协议(Subprotocol)传递。

什么是WebSocket子协议?

WebSocket子协议允许客户端和服务器在建立连接时协商使用的一种或多种应用层协议。这些协议是在WebSocket握手阶段通过Sec-WebSocket-Protocol请求头进行声明的。通过指定子协议,双方能够明确彼此支持的数据格式和消息结构,从而确保高效、准确的信息交换。

子协议的重要性

  1. 提高兼容性:不同的应用程序可能需要不同的数据格式和消息结构。通过定义子协议,可以在同一个WebSocket连接上支持多种应用逻辑,无需为每种情况创建独立的连接。

  2. 简化解析过程:当客户端和服务端都遵循预定义的子协议时,双方都知道如何解析接收到的消息,这大大减少了错误的发生,并且简化了开发者的编码工作。

  3. 增强安全性:特定的子协议可以包含安全相关的规范,比如身份验证、加密等,进一步保障了数据传输的安全性。

如何实现子协议传递?

要实现WebSocket子协议传递,首先需要在客户端发起WebSocket连接请求时,通过设置protocols参数来指定希望使用的子协议列表。例如,在JavaScript中可以通过以下方式:

let ws = new WebSocket('ws://example.com/socket', ['appProtocolV1', 'appProtocolV2']);

而在服务器端,则需要检查客户端提供的子协议列表,并选择一个支持的子协议进行响应。如果服务器选择了某个子协议,则必须将其名称返回给客户端,通常是在HTTP响应头中:

Sec-WebSocket-Protocol: appProtocolV1

一旦连接建立成功,客户端和服务端就可以根据选定的子协议进行通信了。

结论

WebSocket子协议提供了一种灵活且强大的方式来增强Web应用的实时交互能力。通过合理地利用子协议,不仅可以提高应用的兼容性和安全性,还能简化开发流程,降低维护成本。对于致力于打造高质量Web体验的开发者来说,掌握WebSocket子协议传递无疑是一项非常有价值的技术技能。现在就开始探索并应用到你的项目中吧!

案例

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebSocket Test</title>
</head>
<body>
  <h1>WebSocket Test</h1>
  <input type="text" id="messageInput" placeholder="Type a message">
  <button onclick="sendMessage()">Send</button>
  <ul id="messages"></ul>

  <script>
    const ws = new WebSocket('ws://localhost:8080',["json"]);

    ws.onopen = () => {
      console.log('Connected to WebSocket server');
    };

    ws.onmessage = (event) => {
      console.log("event",event)
      const messages = document.getElementById('messages');
      const message = document.createElement('li');
      if (event.data instanceof Blob) {
        // 创建一个新的 FileReader 实例
        const reader = new FileReader();

        // 当文件读取完成时触发此事件
        reader.onload = function(e) {
          // 将读取到的内容赋值给 message.textContent
          // message.textContent = `Received: ${e.target.result}`;
           message.textContent = `Received: ${e.target.result}`;
           messages.appendChild(message);
        };
          // 读取 Blob 作为文本
       reader.readAsText(event.data);
      }
     
    };

    ws.onclose = () => {
      console.log('Disconnected from WebSocket server');
    };

    function sendMessage() {
      const input = document.getElementById('messageInput');
      const message = input.value;
      ws.send(message);
      input.value = '';
    }
  </script>
</body>
</html>
// server.js
const WebSocket = require('ws');

// 创建 WebSocket 服务器,监听 8080 端口
const wss = new WebSocket.Server({ port: 8080 });

// 当有新的客户端连接时触发
wss.on('connection', (ws, request) => {
  console.log('New client connected');

  // 当收到客户端消息时触发
  ws.on('message', (message) => {
     // 获取子协议
  const protocols = request.headers['sec-websocket-protocol'];
  console.log('Connected protocols:', protocols);
    console.log(`Received22: ${message}`);
    // 广播消息给所有连接的客户端
    console.log("wss",wss.clients.size)
    wss.clients.forEach((client) => {
      console.log(client === ws );
     
      // 如果当前客户端不是ws实例,并且客户端的连接状态为已打开
      // if (client !== ws && client.readyState === WebSocket.OPEN) {
         console.log("message",client.readyState,WebSocket.OPEN);
        client.send(message);
      // }
    });
  });

  // 当客户端断开连接时触发
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

console.log('WebSocket server is running on ws://localhost:8080');
{
  "name": "websocket",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ws": "^8.18.0"
  }
}

暂时无法在飞书文档外展示此内容