在读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也不会出现什么问题。。。