nginx+WebSocket踩坑记录

  • 1、场景
  • 2、问题原因
  • 3、解决方法
  • 1、路径未匹配
  • 2、未发送心跳包
  • 3、转发请求配置为websocket链接
  • 4、配置nginx中的读取超时参数
  • 5、wss链接通过nginx转发时,$http_upgrade未取到值,导致转到服务器缺少Upgrade请求头,未识别为websocket链接,导致链接404报错(具体体现:ws链接正常,wss链接404)
  • 其他错误
  • 1、使用wss协议报错
  • 报错信息
  • 问题原因
  • 公网服务器注意事项(使用wss协议)

1、场景

需要反向代理转发websocket链接。

2、问题原因

1、nginx路径未匹配上
2、链接上后,在默认的http链接时长中没有发送心跳包,nginx自动关闭http链接,一般默认为1分钟
3、http链接转发后并没有升级为websockt链接(Bad Request 400错误)
4、websocket长链接1分钟后自动关闭
5、wss链接通过nginx转发时,$http_upgrade未取到值,导致转到服务器缺少Upgrade请求头,未识别为websocket链接,导致链接404报错(具体体现:ws链接正常,wss链接404)

3、解决方法

1、路径未匹配

检查nginx路径配置,如下配置:
正确匹配地址:ws://localhost:8080/websocket/xxx
如果是location /websocket {…}
ws://localhost:8080/websocket/xxx这个地址是匹配不到的,就会报504错误(开启access_log日志可看到)。

worker_processes 1;
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    #日志输出格式
    log_format  main  '$remote_addr - $remote_user [$time_local] "$host:$server_port" "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
    #记录nginx日志
    access_log  logs/access.log  main;
    server {
		listen 8080; 
		server_name localhost;
		
        location /websocket/ {
            proxy_pass http://127.0.0.1:8808/; 
            proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection "upgrade";
        }
    }
}

2、未发送心跳包

1、js文件发送心跳,写一个定时器定时发送心跳信息

var ws;
    ws = new WebSocket("ws://127.0.0.1:8808/websocket/a");

    var heartCheck = {
        timeout: 20000,//20ms,后台设置了60秒过期
        timeoutObj: null,
        serverTimeoutObj: null,
        reset: function(){
            clearTimeout(this.timeoutObj);
            clearTimeout(this.serverTimeoutObj);
            this.start();
        },
        start: function(){
            var self = this;
            this.timeoutObj = setTimeout(function(){
            	ws.send("心跳信息");
                self.serverTimeoutObj = setTimeout(function(){
                    ws.close();//如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
                }, self.timeout);
            }, this.timeout)
        },
    }

	function initWebSorket() {
	    ws.onopen = function () {
	            console.log("连接成功。");
	            heartCheck.start();
	        };
	        ws.onmessage = function (evt) {
	            console.log("发送消息。");
	            heartCheck.reset();
	        };
	        ws.onclose = function (){
	            console.log("连接关闭");
	            reconnct();
	        };
	        ws.onerror = function (){
	            console.log("连接异常");
	            reconnct();
	        };
	}
	
    function reconnct() {
        ws = null;
        ws = new WebSocket("ws://127.0.0.1:8808/websocket/a");
        console.log("重新链接");
        initWebSorket();
    }

3、转发请求配置为websocket链接

配置三行请求头升级为websocket链接
nginx官网文档配置websocket:http://nginx.org/en/docs/http/websocket.html

# 动态决定connection的值,当$http_upgrade为默认值时,则connection的值为keep-alive。当$http_upgrade为websocket时,connection的值为upgrade
		map $http_upgrade $connection_upgrade { 
        	default          keep-alive;  #默认为keep-alive 可以支持 一般http请求
        	'websocket'      upgrade;     #如果为websocket 则为 upgrade 可升级的。
    	}

        location /websocket/ {
            proxy_pass http://127.0.0.1:8808/; 
            #配置以下三行即可
            proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection $connection_upgrade;
        }

4、配置nginx中的读取超时参数

nginx转发请求默认读取数据超时时间为1分钟

location /websocket/ {
            proxy_pass http://127.0.0.1:8808/; 
            #配置以下三行即可
            proxy_http_version 1.1;
			proxy_set_header Upgrade $http_upgrade;
			proxy_set_header Connection "upgrade";
            proxy_read_timeout 300;
        }

5、wss链接通过nginx转发时,$http_upgrade未取到值,导致转到服务器缺少Upgrade请求头,未识别为websocket链接,导致链接404报错(具体体现:ws链接正常,wss链接404)

location /websocket/ {
            proxy_pass http://127.0.0.1:8808/; 
            #配置以下三行即可
            proxy_http_version 1.1;
            # 直接固定Upgrade的值
			proxy_set_header Upgrade "websocket"
			proxy_set_header Connection "upgrade";
            proxy_read_timeout 300;
        }

其他错误

1、使用wss协议报错

报错信息

VM338:1 Mixed Content: The page at ‘’ was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint ‘ws://ip:端口’. This request has been blocked; this endpoint must be available over WSS.

问题原因

1、本身页面的连接为https连接,所以不能用ws协议
解决方法:打开http页面,就可以使用ws协议了

公网服务器注意事项(使用wss协议)

1、服务器未对目标服务开放端口443
解决方法:打开控制面板-安全-防火墙-出入站规则-入站规则-开放端口即可
2、腾讯云安全组未开放端口(腾讯服务器)
解决方法:打开腾讯云服务器管理后台,开放端口即可