WebSocket
WebSocket的初始化代码如下
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(boss, worker)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler())
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("http-codec", new HttpServerCodec());
pipeline.addLast("chunked-write", new ChunkedWriteHandler());
pipeline.addLast("http-aggregator", new HttpObjectAggregator(8192));
pipeline.addLast("http-handler", new WebSocketServerProtocolHandler("/zhny3"));
pipeline.addLast("login-handler", new WebLoginHandler());
pipeline.addLast("json-parse-handler", new WebMsgParseHandler());
pipeline.addLast("web-shunt-handler", new WebShuntHandler());
pipeline.addLast("final-exception-handler", new WebFinalExceptionHandler());
}
});
ChannelFuture f = serverBootstrap.bind(port).sync();
log.info("webSocket服务启动成功!");
f.channel().closeFuture().sync();
log.info("webSocket服务已关闭!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
使用接口测试工具ApiFox测试,url为ws://localhost:8888/zhny3/token/***
这里想校验一下token,处理逻辑在WebLoginHandler里。
结果这里就一直在等待服务端的回复,说明websocket连接未建立。但是此次请求却进入了WebLoginHandler里。经过排查,websocket握手包由客户端到服务器后被封装为FullHttpRequest,所以进入了WebLoginHandler
客户端一直在等待握手报文回复,但是netty内部应该会自动回复握手报文,这里却没有回复。
接着,在WebLoginHandler里手动回复
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(uri,null,false);
WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(request);
if(handshaker == null){
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else{
handshaker.handshake(ctx.channel(),request);
ctx.channel().writeAndFlush(new TextWebSocketFrame("登录成功"));
}
然后,客户端连接成功。但是当再次发送消息,又报了
io.netty.handler.codec.http.websocketx.TextWebSocketFrame cannot be cast to io.netty.handler.codec.http.FullHttpRequest。经过排查,这个错误出现在类WebSocketServerProtocolHandshakeHandler的channelRead方法中
在客户端这里已经建立了连接,然后客户端发的消息到了服务端被封装成了TextWebSocketFrame ,于是在该方法第1行中强转就出现了问题
WebSocketServerProtocolHandshakeHandler中的这个方法的作用是处理WebSocket握手的过程,包括创建握手器、触发握手、监听握手完成事件,并进行相应的处理。
这里就出现了问题,其实客户端第一次发消息就进入了这个方法,客户端的消息被封装为FullHttpRequest,类型正确,然后进入了WebLoginHandler。因为手动回复了握手报文,客户端认为连接建立。于是客户端第二次发送数据,进入服务端后,又进入了channelRead方法,这里就出现了服务端重复进入握手判断逻辑。
又经过测试后发现,客户端发送数据到服务端,服务端就会进入channelRead
在该方法中,如果是第一个握手包,就是FullHttpRequest类型,第1行代码不会出错,于是继续向下执行,接着会判断是否是websocket的报文,判断逻辑如下
如果客户端输入的路径与服务端配置的项目访问路径不一致,就会被当做普通http报文处理,那么就直接handle到下一个处理器,不会回复握手报文。这也就是起初为什么客户端发送连接请求后一直收不到回复而一直等待的原因。当手动回复握手包后,客户端认为建立了websocket连接,于是发送连接建立后的报文,到服务端后报文被封装成TextWebSocketFrame ,于是在channelRead的第一行代码类型转换那里就会报类型无法转换的错误。
正确的解决方法就是将项目客户端输入的路径与服务端配置的项目访问路径一致。如果想要在WebSocket连接建立之前实现信息校验的功能,可以自己实现WebSocket握手逻辑。