一、Netty客户端离线监听
在Netty客户端中,由于网络原因,客户端会与服务端断开连接,但是我们希望客户端能够重新连接服务端,这就需要在客户端中监听客户端是否处于离线状态。
我们可以通过Netty提供的ChannelFuture的方法isDone()和isSuccess()来判断当前客户端是否处于离线状态,并且通过closeFuture()方法来设置关闭处理器,当客户端处于离线状态时,关闭处理器会自动触发客户端重连。
//判断是否连接成功 if (channelFuture.isDone() && channelFuture.isSuccess()) { //连接成功,响应处理逻辑 } else { //连接不成功,客户端离线 //关闭客户端连接 client.shutdown(); //设置关闭处理器 channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { //重连服务端 startClient(); } }); }
二、Netty心跳客户端重连
在Netty客户端中,我们可以使用心跳机制来保持客户端和服务端的连接,当客户端在一段时间内没有接收到来自服务端的心跳包时,客户端可以主动断开连接并重新连接服务端。
我们可以通过Netty提供的IdleStateHandler实现心跳机制,并且在客户端断开连接时触发重连操作。
//客户端连接服务端 public void startClient() { try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)); //添加心跳机制 bootstrap.handler(new ChannelInitializer() { public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast( new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS), new NettyClientHandler() ); } }); ChannelFuture channelFuture = bootstrap.connect().sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } //心跳匹配器触发事件 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; if (event.state() == IdleState.READER_IDLE) { //读取服务端心跳包超时,触发重连操作 startClient(); } else if (event.state() == IdleState.WRITER_IDLE) { //发送心跳包给服务端 ctx.writeAndFlush(new HeartbeatRequestPacket()); } } }
三、Netty客户端连接多个服务端
在Netty客户端中,我们可以同时连接多个服务端,当其中某个服务端离线时,客户端可以自动重连离线服务端,并且支持向在线服务端发送数据。
我们可以在客户端中维护一个服务端列表,并且为每个服务端维护一个Channel对象,在客户端与服务端建立连接后,将Channel对象存储到服务端列表中,并且在客户端与服务端断开连接时将Channel对象从服务端列表中删除。
//多个服务端 private static final MapSERVER_MAP = new ConcurrentHashMap (); //连接服务端 public void connect(final String host, final int port) { try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)); bootstrap.handler(new ChannelInitializer () { public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new NettyClientHandler()); } }); ChannelFuture channelFuture = bootstrap.connect().sync(); if (channelFuture.isSuccess()) { String serverKey = String.format("%s:%d", host, port); SERVER_MAP.put(serverKey, channelFuture.channel()); } channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { String serverKey = String.format("%s:%d", host, port); SERVER_MAP.remove(serverKey); //服务端离线,触发重连操作 reconnect(host, port); } }); } catch (Exception e) { e.printStackTrace(); } } //重连服务端 public void reconnect(final String host, final int port) { executorService.submit(new Runnable() { public void run() { for (;;) { try { TimeUnit.SECONDS.sleep(3); connect(host, port); if (SERVER_MAP.containsKey(String.format("%s:%d", host, port))) { break; } } catch (Exception e) { e.printStackTrace(); } } } }); }
四、如何连接客户端的Netty
在Netty客户端中,我们可以通过BootStrap类来连接服务端。首先,我们需要创建一个BootStrap实例,并且设置相应的参数,例如线程模型、Channel类型和远程地址等。然后,我们需要为BootStrap实例配置一个ChannelHandler处理器实例,在连接成功后,使用ChannelHandler来处理服务端返回的数据流。
//客户端BootStrap public class NettyClient { private EventLoopGroup group = new NioEventLoopGroup(); public void startClient() { try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)); bootstrap.handler(new ChannelInitializer() { public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new NettyClientHandler()); } }); ChannelFuture channelFuture = bootstrap.connect().sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); } } } //客户端ChannelHandler public class NettyClientHandler extends ChannelInboundHandlerAdapter { public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //处理服务端返回数据 } }