news 2026/10/6 13:28:57

从HTTP到WebSocket:如何正确实现协议升级(can ‘upgrade‘ only to ‘websocket‘)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从HTTP到WebSocket:如何正确实现协议升级(can ‘upgrade‘ only to ‘websocket‘)


背景痛点:为什么浏览器突然翻脸

初学 WebSocket 时,我刷新页面看到控制台飘红
Error: can upgrade only to websocket
第一反应是“我明明写了ws://呀?”
其实这句话不是浏览器傲娇,而是服务器在握手阶段拒绝升级。
触发场景通常只有三类:

  • 少了Connection: Upgrade或Upgrade: websocket头
  • 服务端返回的状态码不是 101 Switching Protocols
  • 反向代理(Nginx、Kong 等)把 Upgrade 头吃掉

只要任意一项不满足,浏览器就会抛出这条错误,后续帧直接罢工。
理解 HTTP 升级机制后,你会发现 WebSocket 并不是“另一端口”,而是“同端口、不同语法”——先假装自己是 HTTP,拿到 101 车票后,再“变脸”成二进制帧协议。

技术对比:长轮询、SSE 还是 WebSocket?

维度HTTP 长轮询Server-Sent EventsWebSocket
协议HTTP/1.1HTTP/1.1HTTP→Upgrade→WS
方向双向模拟仅服务端推送真正双向
延迟1~3 s(轮询间隔)<1 s<100 ms
开销每次带 HTTP 头带少量头帧头仅 2~14 B
适用场景低版本 IE 兼容新闻/股价推送实时游戏、通话、协同编辑

一句话总结:
“能 Upgrade 就别轮询,SSE 是单向备胎,WebSocket 才是双向正主。”

核心实现:让服务器说“101”

1. 标准握手流程(必须字段)

客户端请求头:

GET /chat HTTP/1.1 Host: example.com:8080 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13

服务端校验后返回:

HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Key→Accept 的算法固定:
base64(sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
任何偏差都会触发can upgrade only to websocket。

2. Node.js 示例(ws 库)

安装:

npm i ws

server.js:

const WebSocket = require('ws'); const http = require('http'); // 先跑一个普通 HTTP 服务器 const server = http.createServer((req, res) => { res.writeHead(200); res.end('WebSocket upgrade server'); }); const wss = new WebSocket.Server({ noServer: true }); // 监听协议升级事件 server.on('upgrade', (request, socket, head) => { // 手动校验必要头 if ( request.headers.upgrade !== 'websocket' || !request.headers.connection.includes('Upgrade') ) { socket.write('HTTP/1.1 400 Bad Request\r\n\r\n'); socket.destroy(); return; } wss.handleUpgrade(request, socket, head, (ws) => { wss.emit('connection', ws, request); }); }); wss.on('connection', (ws) => { console.log('[WS] 客户端已连接'); ws.on('message', (data) => { console.log('[WS] 收到:%s', data); ws.send(`服务端回声:${data}`); }); ws.on('error', (err) => console.error('[WS] 异常:', err.message)); }); server.listen(8080, () => console.log('HTTP+WS 服务已启动于 8080'));

关键注释已写中文,异常直接try/catch会漏掉帧级错误,因此统一在error事件里处理。

3. Python 示例(websockets 库)

安装:

pip install websockets

server.py:

import asyncio import websockets import logging logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s") async def echo(websocket, path): remote = websocket.remote_address logging.info(f"[WS] 客户端 {remote} 连接成功") try: async for msg in websocket: logging.info(f"[WS] 收到:{msg}") await websocket.send(f"服务端回声:{msg}") except websockets.exceptions.ConnectionClosed: logging.info(f"[WS] 客户端 {remote} 已断开") except Exception as e: logging.error(f"[WS] 异常:{e}") # 官方库已封装好 101 校验,直接启动即可 start_server = websockets.serve(echo, "0.0.0.0", 8081) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()

如果想手动校验 Upgrade 头,可在process_request钩子内拦截,返回426 Upgrade Required。

生产考量:别让握手成功倒在最后一公里

  1. 连接超时与重连

    • 建议客户端 55 秒无响应就主动重连,避开默认 60 秒 NAT 超时
    • 服务端启用ping/pong心跳,Node 版ws自动回pong,Python 需await websocket.pong()
  2. WSS 加密配置

    • 证书放在反向代理层最省事,Nginx 监听 443,后端 8080 走裸 WS 即可
    • 若直连,需要wss://+ssl.create_default_context(),别忘了把中间证书链补全
  3. 负载均衡器特殊配置
    Nginx 示例:

map $http_upgrade $connection_upgrade { default upgrade; '' close; } server { listen 443 ssl; location /ws { proxy_pass http://upstream_ws; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 300s; # 给心跳留时间 } }

漏掉任何一行,都会把 Upgrade 头吞掉,浏览器继续报错。

避坑指南:426/400/Origin 一个都别放过

  • 426 Upgrade Required
    服务端显式告诉客户端“我拒绝非 WebSocket 流量”,检查是否误把/ws当普通 HTTP 路由
  • 400 Bad Request
    常见把Sec-WebSocket-Key大小写写错,或Accept计算错误
  • Origin 校验陷阱
    浏览器一定会带 Origin 头,但 Node 默认不检查。生产环境务必做白名单,防止被恶意网页跨域连进来
  • 心跳间隔
    内网 30 s,公网 45~60 s 足够,太频繁浪费流量,太长容易被防火墙 RST

延伸思考:QUIC 时代的 WebTransport

WebSocket 建立在 TCP 之上,队头阻塞依旧存在。
Chrome 最新版已开放 WebTransport API,走 HTTP/3 over QUIC,天然多路复用、0-RTT 握手。
思路相同:先fetch()协商,再createBidirectionalStream()拿到读写流。
如果你已经把 Upgrade 玩熟,不妨把实验目标换成 WebTransport,提前踩坑,等标准落地就能无缝迁移。

写在最后:把“升级”真正跑通

从 HTTP 到 WebSocket,其实就是一次“换票上车”的小动作,但票根(101 状态)、身份证(Upgrade 头)、座位号(Sec-WebSocket-Key)一个都不能少。
我在本地调试通过后,第一时间把代码丢到线上,结果还是被 Nginx 打脸——忘了加proxy_set_header,浏览器继续can upgrade only to websocket。
踩完坑最大的感受是:协议文档写得很细,但魔鬼藏在“反向代理默认不转发”这种小字里。

如果你想亲手把“耳朵、大脑、嘴巴”串成一条完整的实时通话链路,又懒得自己搭 ASR、LLM、TTS,可以试试这个动手实验:
从0打造个人豆包实时通话AI
实验把火山引擎的豆包系列模型都封装好了,WebSocket 升级部分也给了现成模板,小白直接跑脚本就能在浏览器里跟 AI 语音聊天。
我跟着做完,把聊天角色改成“猫娘”音色,30 分钟搞定,比自己东拼西凑省了不少时间。
升级协议只是第一步,真正的乐趣是让 AI 开口说话那一刻——祝你也能一次 101,永不 426。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 10:17:50

3天入门SAR数据处理:GMTSAR开源工具实战指南

3天入门SAR数据处理&#xff1a;GMTSAR开源工具实战指南 【免费下载链接】gmtsar GMTSAR 项目地址: https://gitcode.com/gh_mirrors/gmt/gmtsar 合成孔径雷达分析技术正深刻改变着地形形变监测领域的研究范式。GMTSAR作为一款融合通用制图工具&#xff08;GMT&#xff…

作者头像 李华
网站建设 2026/10/4 10:17:37

7天搭建高效运转的个人知识管理系统:从信息焦虑到认知升级

7天搭建高效运转的个人知识管理系统&#xff1a;从信息焦虑到认知升级 【免费下载链接】Obsidian-Templates A repository containing templates and scripts for #Obsidian to support the #Zettelkasten method for note-taking. 项目地址: https://gitcode.com/gh_mirrors…

作者头像 李华
网站建设 2026/10/4 10:17:57

Hunyuan-MT-7B-WEBUI性能优化实践,首词延迟低于200ms

Hunyuan-MT-7B-WEBUI性能优化实践&#xff1a;首词延迟低于200ms的工程落地路径 在实际部署腾讯混元开源翻译模型时&#xff0c;很多用户反馈&#xff1a;模型虽强&#xff0c;但首次响应慢、连续翻译卡顿、高并发下延迟飙升——尤其在政务、教育、跨境等对实时性敏感的场景中&…

作者头像 李华
网站建设 2026/10/4 10:17:37

MedGemma 1.5环境部署:Ubuntu 22.04 + CUDA 12.1 + PyTorch 2.3 兼容配置

MedGemma 1.5环境部署&#xff1a;Ubuntu 22.04 CUDA 12.1 PyTorch 2.3 兼容配置 1. 为什么MedGemma 1.5需要一套“严丝合缝”的本地环境&#xff1f; 你可能已经试过直接 pip install 一个医疗大模型&#xff0c;结果卡在 CUDA 版本不匹配、PyTorch 报错 torch.cuda.is_av…

作者头像 李华
网站建设 2026/10/4 3:13:18

ChatTTS延迟与质量权衡:不同参数组合实测

ChatTTS延迟与质量权衡&#xff1a;不同参数组合实测 1. 引言&#xff1a;为什么“快”和“真”总在打架&#xff1f; 你有没有试过这样&#xff1a;刚点下“生成语音”&#xff0c;屏幕还卡着&#xff0c;心里已经开始默念“怎么还没好”&#xff1b;等终于播出来&#xff0…

作者头像 李华
网站建设 2026/10/5 4:40:38

SeqGPT-560M在医疗报告处理中的应用:疾病/药品/剂量精准识别案例

SeqGPT-560M在医疗报告处理中的应用&#xff1a;疾病/药品/剂量精准识别案例 1. 为什么医疗报告需要“零幻觉”的信息提取&#xff1f; 你有没有见过这样的场景&#xff1a;医生刚写完一份门诊记录&#xff0c;护士要手动把“高血压、阿司匹林 100mg 每日一次、氯沙坦钾 50mg…

作者头像 李华