netty中的一个小bug

来源:互联网 发布:mysql数据库读写分离 编辑:程序博客网 时间:2024/04/29 06:46
在读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也不会出现什么问题。。。



原创粉丝点击