websocket以及升级

WebSocket 是 HTML5 推出的全双工、长连接网络协议,解决了 HTTP "请求 - 响应" 模型下无法实现服务器主动推送数据的痛点(如实时聊天、股票行情、直播弹幕)。本文会从核心原理、HTTP 升级流程、代码示例、核心约束四个维度,完整拆解 WebSocket。

一、WebSocket 核心基础

1. 核心特性(对比 HTTP)

HTTP 是「半双工、短连接」:客户端发请求 → 服务器回响应,响应结束连接断开,服务器无法主动给客户端发数据;WebSocket 是「全双工、长连接」:连接建立后,客户端和服务器可同时、双向发送数据,连接持续保持,无需频繁重连。

特性 HTTP WebSocket
通信方向 半双工(客户端主动) 全双工(双向主动)
连接方式 短连接(请求完断开) 长连接(持续保持)
数据格式 带完整 HTTP 头 轻量帧格式(无冗余头)
服务器推送 不支持 原生支持
协议标识 http/https ws/wss(加密版)

2. 应用场景

  • 实时聊天(如微信网页版、在线客服);
  • 实时数据推送(如股票行情、区块链价格);
  • 多人协作(如在线文档、白板);
  • 直播弹幕、游戏实时交互。

二、从 HTTP 升级到 WebSocket 的完整流程

WebSocket 连接的建立必须通过 HTTP 协议 "升级"(RFC 6455 规范),核心是客户端发送「升级请求」,服务器确认后切换协议,整个过程分为 7 步:

前置说明

  • WebSocket 升级基于 TCP 连接(先完成 TCP 三次握手,再谈协议升级);
  • 升级成功后,原 HTTP 连接变为 WebSocket 连接,协议标识从 http 变为 ws(加密版为 wss,对应 HTTPS);
  • 核心升级头:Upgrade: websocket + Connection: Upgrade(告知服务器 "要切换协议")。

完整升级流程(以 ws://example.com/chat 为例)

关键步骤拆解(核心字段说明)

步骤 2:客户端发送升级请求(核心头解析)

客户端的 HTTP 请求必须包含以下关键头,缺一不可:

复制代码
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket          # 核心:请求升级为 WebSocket 协议
Connection: Upgrade         # 核心:配合 Upgrade,标识连接要切换
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==  # 随机字符串,用于验证(防伪造)
Sec-WebSocket-Version: 13   # 必须为 13(最新标准版本)
Sec-WebSocket-Protocol: chat, superchat  # 可选:指定子协议(如聊天专用协议)
Sec-WebSocket-Extensions: permessage-deflate  # 可选:压缩扩展
  • Sec-WebSocket-Key:客户端生成的随机 Base64 字符串,服务器需用它生成 Sec-WebSocket-Accept,防止 "假升级请求";
  • Sec-WebSocket-Version:13:唯一合法的版本号(老版本已废弃)。
步骤 3:服务器验证请求

服务器必须做 3 个核心验证,否则拒绝升级:

  1. 校验 Sec-WebSocket-Version 是否为 13;
  2. 校验 Upgrade: websocketConnection: Upgrade 是否存在;
  3. 计算 Sec-WebSocket-Accept
    • 拼接:Sec-WebSocket-Key + 固定字符串 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
    • 哈希:对拼接结果做 SHA1 哈希;
    • 编码:将哈希结果做 Base64 编码,得到 Sec-WebSocket-Accept
步骤 4:服务器返回 101 响应

验证通过后,服务器返回 101 Switching Protocols 响应(HTTP 状态码 101 表示 "协议切换"):

复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=  # 服务器计算后的结果
Sec-WebSocket-Protocol: chat  # 可选:确认使用的子协议
步骤 5:客户端验证响应

客户端会用相同算法计算 Sec-WebSocket-Accept,对比服务器返回的值:

  • 一致:验证通过,WebSocket 连接建立;
  • 不一致:客户端断开连接,升级失败。
步骤 6:双向通信

连接建立后,双方不再使用 HTTP 报文,而是用 WebSocket 帧 传输数据:

  • 帧格式轻量(仅包含操作码、长度、数据),无 HTTP 头的冗余开销;
  • 支持文本、二进制数据传输;
  • 可发送 "PING/PONG" 帧做心跳检测(保持连接)。

这两步是为了避免服务器误升级或者中间人伪造升级相应。

三、HTTP 升级到 WebSocket 的代码示例

以下是基于 Node.js + ws 库 (服务端)和 浏览器原生 WebSocket(客户端)的完整可运行示例:

1. 服务端代码(Node.js)

javascript 复制代码
// 1. 安装依赖:npm install ws
const WebSocket = require('ws');
const http = require('http');

// 第一步:创建 HTTP 服务器(WebSocket 升级基于 HTTP 服务器)
const server = http.createServer((req, res) => {
  res.writeHead(200);
  res.end('普通 HTTP 请求');
});

// 第二步:创建 WebSocket 服务器,挂载到 HTTP 服务器上
const wss = new WebSocket.Server({ server });

// 第三步:监听 WebSocket 连接
wss.on('connection', (ws) => {
  console.log('客户端已建立 WebSocket 连接');

  // 监听客户端发送的消息
  ws.on('message', (data) => {
    console.log('收到客户端消息:', data.toString());
    // 服务器主动给客户端发消息(全双工)
    ws.send(`服务器回复:${data.toString()}`);
  });

  // 监听连接关闭
  ws.on('close', () => {
    console.log('WebSocket 连接已关闭');
  });

  // 监听错误
  ws.on('error', (err) => {
    console.error('WebSocket 错误:', err);
  });
});

// 启动服务器,监听 8080 端口
server.listen(8080, () => {
  console.log('服务器运行在 http://localhost:8080');
  console.log('WebSocket 地址:ws://localhost:8080');
});

2. 客户端代码(浏览器 HTML)

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>WebSocket 示例</title>
</head>
<body>
  <input type="text" id="msgInput" placeholder="输入消息">
  <button onclick="sendMsg()">发送</button>
  <div id="msgList"></div>

  <script>
    // 第一步:创建 WebSocket 实例(自动触发 HTTP 升级流程)
    const ws = new WebSocket('ws://localhost:8080');

    // 第二步:监听 WebSocket 事件
    // 1. 连接成功事件
    ws.onopen = () => {
      console.log('WebSocket 连接已建立');
      appendMsg('连接成功:可以发送消息了');
    };

    // 2. 接收服务器消息事件
    ws.onmessage = (event) => {
      appendMsg(`服务器:${event.data}`);
    };

    // 3. 连接关闭事件
    ws.onclose = (event) => {
      appendMsg(`连接关闭:${event.code} - ${event.reason}`);
    };

    // 4. 错误事件
    ws.onerror = (error) => {
      appendMsg(`错误:${error.message}`);
    };

    // 发送消息函数
    function sendMsg() {
      const input = document.getElementById('msgInput');
      const msg = input.value.trim();
      if (!msg) return;
      // 客户端发送消息给服务器
      ws.send(msg);
      appendMsg(`客户端:${msg}`);
      input.value = '';
    }

    // 辅助函数:追加消息到页面
    function appendMsg(text) {
      const msgList = document.getElementById('msgList');
      const p = document.createElement('p');
      p.textContent = text;
      msgList.appendChild(p);
    }
  </script>
</body>
</html>

运行步骤

  1. 启动服务端:node server.js
  2. 打开客户端 HTML 文件(直接双击或用浏览器打开);
  3. 在输入框输入消息,点击 "发送",即可看到客户端与服务器的双向实时通信。

四、WebSocket 的核心约束(使用注意事项)

1. 协议层面约束

  • 必须通过 HTTP/HTTPS 升级:无法直接建立 WebSocket 连接,必须先发起 HTTP 升级请求,且仅支持 GET 方法;
  • 版本强制 :仅 Sec-WebSocket-Version:13 合法,老版本(如 8、12)已被废弃,服务器需拒绝非 13 版本的请求;
  • 跨域规则 :遵循与 HTTP 相同的跨域规则,若需跨域,服务器需配置 Access-Control-Allow-Origin 头;
  • 加密版(wss) :生产环境必须用 wss://(对应 HTTPS 升级),ws:// 明文传输存在数据泄露风险,且部分浏览器 / 服务器会拦截。

2. 连接层面约束

  • 长连接资源占用:每个 WebSocket 连接是长连接,服务器需控制最大连接数(避免内存耗尽);
  • 心跳检测:网络波动可能导致连接 "假死",需定期发送 PING/PONG 帧(客户端发 PING,服务器回 PONG),超时则重连;
  • 断开重连:连接可能因网络、服务器重启断开,客户端需实现自动重连逻辑;
  • 帧大小限制:服务器通常限制单帧数据大小(如 1MB),避免大文件传输压垮服务器(大文件建议用 HTTP 分段传输)。

3. 服务器 / 环境约束

  • 反向代理配置 :若前端通过 Nginx 反向代理访问 WebSocket,需配置 Nginx 支持 WebSocket 升级:

    复制代码
    server {
      listen 80;
      server_name example.com;
    
      location /chat {
        proxy_pass http://localhost:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;  # 关键:传递升级头
        proxy_set_header Connection "upgrade";  # 关键:传递连接头
        proxy_set_header Host $host;
        proxy_read_timeout 86400;  # 延长超时时间(长连接)
      }
    }
  • 防火墙 / 负载均衡:需放行 WebSocket 端口,且负载均衡器需支持 "粘性会话"(确保同一客户端的连接始终路由到同一服务器)。

4. 数据传输约束

  • 数据格式:支持文本(UTF-8)和二进制数据,但需双方约定格式(如 JSON 文本、Protobuf 二进制);
  • 无内置重传:WebSocket 帧传输无重传机制,若数据丢失,需上层业务实现重传逻辑;
  • 流量控制:全双工传输可能导致数据积压,需实现流量控制(如客户端 / 服务器缓存满时暂停发送)。

五、核心总结

  1. WebSocket 本质:基于 TCP 长连接的全双工协议,通过 HTTP 升级流程建立,解决了 HTTP 无法服务器主动推送的问题;
  2. 升级核心 :客户端发送 Upgrade: websocket 头的 GET 请求,服务器验证后返回 101 响应,协议切换为 ws/wss;
  3. 关键验证 :通过 Sec-WebSocket-Key/Sec-WebSocket-Accept 防止伪造请求,确保升级合法性;
  4. 核心约束:生产环境用 wss 加密、实现心跳检测和重连、配置反向代理支持升级、控制连接数和帧大小。

0voice · GitHub

相关推荐
航Hang*7 分钟前
第二章:综合布线技术 —— 综合布线常用器材和工具
网络·期末·复习
Exclusive_Cat32 分钟前
先声医疗面经
网络
七夜zippoe42 分钟前
异步编程实战:构建高性能Python网络应用
开发语言·python·websocket·asyncio·aiohttp
llilian_161 小时前
时间同步校时服务器配件清单及挑选攻略 校时时间服务器 网络时间同步装置
运维·服务器·网络
nvd111 小时前
通过 Gmail API 发送邮件的完整指南
服务器·网络
duration~1 小时前
ARP 协议详情
网络·网络协议·tcp/ip·智能路由器
zbtlink1 小时前
常见的家用路由器耗电量高吗?不同产品耗电量会不会有差别
网络·智能路由器
w_t_y_y1 小时前
http通信鉴权(三)基于 Session + CSRF Token 的 Cookie 认证
网络协议·http·csrf
渡我白衣1 小时前
Reactor与多Reactor设计:epoll实战
linux·网络·人工智能·网络协议·tcp/ip·信息与通信·linux网络编程
灋✘逞_兇2 小时前
Protobuf的RPC序列化和调用原理
网络·网络协议·rpc