您的当前位置:首页正文

nginx转发websocket请求,客户端报错:{code: 1006, reason: “abnormal closure“}

2024-11-26 来源:个人技术集锦

最近写了一个小程序,使用netty作为后端框架搭建了一个聊天服务,协议用的webSocket,本地调试好后,部署到测试服,使用nginx反向代理服务器。这时候客户端出现报错:{code: 1006, reason: “abnormal closure”}。

碰到这样的问题,本能地想要祭出试错大法:在网络上查找方案,然后不断地尝试看看能否解决问题。最近看过netty是如何完成websocket连接的源码,所以心想是不是可以自己去解决这个问题。

说干就干,第一步自然是加上日志。重写了WebSocketServerProtocolHandler的exceptionCaught方法。

@Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("Exception: ", cause);
        super.exceptionCaught(ctx, cause);
    }

这时候才看到报错,可以猜到是header参数错了,但不确定是哪个地方。

2024-10-10 14:16:23.518 ERROR 41226 --- [ntLoopGroup-3-4] c.s.n.n.MyWebSocketServerProtocolHandler : Exception: 

io.netty.handler.codec.http.websocketx.WebSocketServerHandshakeException: not a WebSocket request: a |Connection| header must includes a token 'Upgrade'
	at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker13.newHandshakeResponse(WebSocketServerHandshaker13.java:140) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:202) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:278) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:256) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandshakeHandler.channelRead(WebSocketServerProtocolHandshakeHandler.java:97) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]

nginx配置如下:

location /netty/ {
                        proxy_redirect off;
                        proxy_pass http://wss_psy/;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real_IP $remote_addr;
                        proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
                        proxy_http_version 1.1;
                        proxy_connect_timeout   65;
                        proxy_send_timeout      65;
                        proxy_read_timeout      300;
                        proxy_ignore_client_abort on;  #客户端断网时,nginx服务器是否终端对被代理服务器的请求。默认为off。
                        proxy_buffering                 off;
                        #keepalive_requests             8192;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection “upgrade”;
                }

继续加日志,在HttpObjectAggregator解析出http消息之后,加了一个HttpHeaderHandler用于输出header参数:

serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new HttpServerCodec())
                                    .addLast(new ChunkedWriteHandler())
                                    .addLast(new HttpObjectAggregator(65536))
                                    .addLast(new HttpHeaderHandler())		//输出header参数
//                                    .addLast(new WebSocketServerProtocolHandler("/ws"))
                                    .addLast(new MyWebSocketServerProtocolHandler("/ws"))
                                    .addLast(new WebsocketServerHandler());
                        }
                    });
@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            LOGGER.info("url: {}", request.uri());
            Iterator<Map.Entry<String, String>> iterator = request.headers().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                LOGGER.info("{}: {}", entry.getKey(), entry.getValue());
            }
            super.channelRead(ctx, msg);
        } else {
            super.channelRead(ctx, msg);
        }
    }

日志输出如下:

2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : url: /ws
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Host: psy.zhihe.site
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : X-Real_IP: 103.91.179.202
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : X-Forwarded-For: 103.91.179.202:49343
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Upgrade: websocket
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Connection: âupgradeâ 
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Pragma: no-cache
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Cache-Control: no-cache
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : User-Agent: wechatdevtools desktopapp appservice port/51058 token/c5012f423f12b6a93f67a0ef768a66c7 runtime/2 MicroMessenger
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Origin: https://psy.zhihe.site
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Sec-WebSocket-Version: 13
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Accept-Encoding: gzip, deflate, br
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Accept-Language: zh-CN,zh;q=0.9
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : referer: https://servicewechat.com/wx271a8a9364230880/devtools/page-frame.html
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Sec-WebSocket-Key: aWZOjVrQAyZl0iBc7VTHbA==
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
2024-10-10 14:29:05.912  INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor          : content-length: 0

就此可以看出是header中的Connection参数出了问题,几经修改后问题解决,最后更正如下:

location /netty/ {
                        proxy_redirect off;
                        proxy_pass http://wss_psy/;
                        proxy_set_header Host $host;
                        proxy_set_header X-Real_IP $remote_addr;
                        proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
                        proxy_http_version 1.1;
                        proxy_connect_timeout   65;
                        proxy_send_timeout      65;
                        proxy_read_timeout      300;
                        proxy_ignore_client_abort on;  #客户端断网时,nginx服务器是否终端对被代理服务器的请求。默认为off。
                        proxy_buffering                 off;
                        #keepalive_requests             8192;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection Upgrade;
                }

nginx配置是从另外一个项目抄过来的,另外一个项目没有出现问题,也许是netty版本不同,未作深究。

显示全文