netty中的一个小bug

在处理WebSocket协议时,当接收到客户端的关闭连接帧,Netty需要返回相应帧并关闭底层socket。然而,由于握手对象-handshaker在不同版本的WebSocket中处理方式不一致,导致在获取handshaker时可能为null。错误发生在尝试从WebSocketServerProtocolHandler的context中获取handshaker,而实际上它存储在WebSocketServerProtocolHandshakeHandler的context中,引发异常。幸运的是,WebSocketServerProtocolHandler的exceptionCaught方法会捕获这个null异常并关闭channel,从而避免问题暴露。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在读netty的websocket处理的handler部分的代码的时候,发现了一个小bug,不过这个bug不会造成太大的影响,我们来看看WebSocketServerProtocolHandler的decode部分的代码:
    @Override
    protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, List<Object> out) throws Exception {
        if (frame instanceof CloseWebSocketFrame) {  //如果是用于关闭
            WebSocketServerHandshaker handshaker = getHandshaker(ctx);
            frame.retain();
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); //发送用于关闭的websocket帧
            return;
        }
        super.decode(ctx, frame, out);  
    }
这里


这里当收到的客户端的帧是用于关闭连接的话,按照websocket协议,需要向客户端返回这个帧,然后关闭底层的socket连接,这里是获取以前用到的用于websocket建立连接的握手对象来处理这个帧的,问题就处在这里,因为其实最后获取这个handshaker对象将会是null的,我们来看看获取这个方法的定义:

    static WebSocketServerHandshaker getHandshaker(ChannelHandlerContext ctx) {
        return ctx.attr(HANDSHAKER_ATTR_KEY).get();
       
    }

那么我们再来看看是什么时候set的吧,他是在WebSocketServerProtocolHandshakeHandler里面设置的,前面已经提到,不同版本的websocket需要对应的握手对象来处理,直接来看看代码吧:

    @Override
    public void channelRead(final ChannelHandlerContext ctx, Object msg) throws Exception {
        FullHttpRequest req = (FullHttpRequest) msg;
        if (req.getMethod() != GET) {  //如果不是get请求,那么就出错了
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
            return;
        }
        //创建爱你websocket进行连接握手的工厂类,因为不同版本的连接握手不太一样
        final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
                getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions);
        final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);  //这里会根据不同的websocket版本来安排不同的握手handler
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
        } else {
        	//其实这里在将用于建立连接的http报文发送回去之后,会将前面添加的http部分的handler都移除,然后加上用于decode和encode针对websocket帧的handler
            final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);   //这里说白了就是进行握手,向客户端返回用于建立连接的报文
            handshakeFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (!future.isSuccess()) {
                        ctx.fireExceptionCaught(future.cause());
                    } else {
                        ctx.fireUserEventTriggered(  //用于激活握手已经完成的事件,可以让用户的代码收到通知
                                WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
                    }
                }
            });
            WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker);   //记录用到的用于握手的对象
            ctx.pipeline().replace(this, "WS403Responder",
                    WebSocketServerProtocolHandler.forbiddenHttpRequestResponder());  //将当前这个handler替换,因为只有刚开始使用http协议进行通信,接下来就没有了
        }
    }

在这里,将从factory中获取的shaker对象保存起来,我们知道么一个handlercontext其实也是一个attributemap对象,那么也就是将这个shaker对象放到了当前的ctx中,

而上面获取shaker的时候,却是在WebSocketServerProtocolHandler的context中获取,那么必然将会获取null,这个异常将会被WebSocketServerProtocolHandler的exceptionCaught方法来处理,而这里正好就是将channel关闭了,因此正好,这个bug也不会出现什么问题。。。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值