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.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com 

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

Upgrade: websocket
Connection: 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; 
     }
 }
posted @ 2020-03-29 21:49  趙小傑  阅读(39989)  评论(0编辑  收藏  举报