2026/2/14 22:48:02
网站建设
项目流程
网站建设 验收意见,百度收录规则2022,湖南郴州市有几个县,网站建设 三网背景痛点#xff1a;为什么浏览器突然翻脸
初学 WebSocket 时#xff0c;我刷新页面看到控制台飘红 Error: can upgrade only to websocket 第一反应是“我明明写了 ws:// 呀#xff1f;” 其实这句话不是浏览器傲娇#xff0c;而是服务器在握手阶段拒绝升级。 触发场景通…背景痛点为什么浏览器突然翻脸初学 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 s100 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: s3pPLMBiTxaQ9kYGzzhZRbKxOoKey→Accept 的算法固定base64(sha1(key 258EAFA5-E914-47DA-95CA-C5AB0DC85B11))任何偏差都会触发can upgrade only to websocket。2. Node.js 示例ws 库安装npm i wsserver.jsconst 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(HTTPWS 服务已启动于 8080));关键注释已写中文异常直接try/catch会漏掉帧级错误因此统一在error事件里处理。3. Python 示例websockets 库安装pip install websocketsserver.pyimport asyncio import websockets import logging logging.basicConfig(levellogging.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。生产考量别让握手成功倒在最后一公里连接超时与重连建议客户端 55 秒无响应就主动重连避开默认 60 秒 NAT 超时服务端启用ping/pong心跳Node 版ws自动回pongPython 需await websocket.pong()WSS 加密配置证书放在反向代理层最省事Nginx 监听 443后端 8080 走裸 WS 即可若直连需要wss://ssl.create_default_context()别忘了把中间证书链补全负载均衡器特殊配置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 时代的 WebTransportWebSocket 建立在 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。