写点什么

WebSocket 连接错误 Error during WebSocket handshake Unexpected response code 404

发布于: 2020 年 11 月 03 日
WebSocket连接错误Error during WebSocket handshake Unexpected response code 404

WebSocket 连接错误 Error during WebSocket handshake Unexpected response code 404

一、问题描述

后台 SpringBoot 使用 @ServerEndpoint 创建了一个 websocket 服务端,本地测试的时候一切正常,部署到线上的时候链接报错

复制WebSocket connection to 'ws://xxxx' failed: Error during WebSocket handshake: Unexpected response code: 404
复制代码

当项目使用域名+端口号的方式访问的时候 ws 连接正常,而通过 nginx 反向代理后 ws 连接就不正常了。

错误的 nginx 配置:

复制server{    listen  80;    charset utf-8;    server_name ws.xxx.cn;     proxy_set_header Host $host:$server_port;     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header REMOTE-HOST $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     client_max_body_size 100m;     location /  {        proxy_pass http://127.0.0.1:8087;      } }
复制代码

二、原因分析

Websocket 握手格式包:

复制GET /chat HTTP/1.1Host: server.example.comUpgrade: websocketConnection: UpgradeSec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==Sec-WebSocket-Protocol: chat, superchatSec-WebSocket-Version: 13Origin: http://example.com 
复制代码

这请求类似 http 协议,里面多了陌生的内容是:

复制Upgrade: websocketConnection: Upgrade
复制代码

这个就是 Websocket 的相关的,他会告诉 Apache、Nginx 等服务器我发起的是 websocket 请求,不是 http!下面的三个参数 Sec-WebSocket-Key、Sec-WebSocket-Protocol、Sec-WebSocket-Version 作用大概就是验证请求确实是 websocket,同时指定协议版本。

三、解决方法

官方地址:http://nginx.org/en/docs/http/websocket.html

nginx 配置 WebSocket 代理

要将客户端和服务器之间的连接从 HTTP / 1.1 转换为 WebSocket,将使用 HTTP / 1.1 中可用的协议切换机制。

但是,有一个微妙之处:由于“升级”是 逐跳的 标头,因此它不会从客户端传递到代理服务器。使用正向代理,客户端可以使用该 CONNECT 方法来规避此问题。但是,这不适用于反向代理,因为客户端不知道任何代理服务器,并且需要对代理服务器进行特殊处理。

从版本 1.3.13 开始,nginx 实施了特殊的操作模式,如果代理服务器返回了代码为 101(交换协议)的响应,并且客户端通过以下方式请求协议切换,则允许在客户端与代理服务器之间建立隧道。请求中的“升级”标头。

如上所述,包括“ Upgrade”和“ Connection”的逐跳标头未从客户端传递到代理服务器,因此,为了使代理服务器了解客户端将协议切换到 WebSocket 的意图,这些标头必须明确传递:

复制location /chat/ {    proxy_pass http://backend;    proxy_http_version 1.1;    proxy_set_header Upgrade $http_upgrade;    proxy_set_header Connection "upgrade";}
复制代码

默认情况下,如果代理服务器在 60 秒内未传输任何数据,则连接将关闭。可以使用 proxy_read_timeout 指令来增加此超时时间 。或者,可以将代理服务器配置为定期发送 WebSocket ping 帧以重置超时并检查连接是否仍然有效。

如果不配置超时时间,隔一会就会断开 具体超时时间具体要根据业务来调整。最终的配置如下:

server{    listen  80;    charset utf-8;    server_name ws.xxx.cn;     proxy_set_header Host $host:$server_port;     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header REMOTE-HOST $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     client_max_body_size 100m;     location /  {        proxy_pass http://127.0.0.1:8087;         proxy_http_version 1.1;        proxy_set_header Upgrade $http_upgrade;        proxy_set_header Connection "upgrade";        proxy_connect_timeout 4s;         proxy_read_timeout 7200s;         proxy_send_timeout 12s;      } }
复制代码


用户头像

我们始于迷惘,终于更高的迷惘. 2020.03.25 加入

一个酷爱计算机技术、健身运动、悬疑推理的极客狂人,大力推荐安利Java官方文档:https://docs.oracle.com/javase/specs/index.html

评论

发布
暂无评论
WebSocket连接错误Error during WebSocket handshake Unexpected response code 404