当前位置: 首页 > news >正文

Netty 连接存活检测——如何判断连接是否断开?

一.前言

最近碰到了一个需求,需要判断客户端与服务端之间的连接是否已经断开,查阅资料后发现Netty提供了如下几种方式,现总结如下,方便后续查看。

二.存活检测方式

2.1 SO_KEEPALIVE选项

Netty提供了SO_KEEPALIVE选项,当连接保持空闲一段时间时,TCP 会自动向远程对等方发送保持活动探测。但由于该时间间隔依赖于操作系统(通常默认值为2小时),因此此种方式不太被推荐使用。

2.2 IdleStateHandler

Channel在一段时间内没有执行读或写操作时,会触发IdleStateEvent

IdleStateHandler关于读写空闲多久触发IdleStateEvent包含如下三个参数:

  • readerIdleTime:读空闲时间, 超过设定的时间,会触发IdleState.READER_IDLE,若设置为0表示禁用。
  • writerIdleTime:写空闲时间, 超过设定的时间,会触发IdleState.WRITE_IDLE,若设置为0表示禁用。
  • allIdleTime:读写都空闲时间,超过设定时间,会触发IdleState.ALL_IDLE,若设置为0表示禁用。

上述IdleStateEvent可以在方法public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception中捕获。

下面是一个客户端应用IdleStateHandler的示例,在该示例中,若30秒没有读写事件时,客户端会连续向服务端发送 3次PING,每次间隔10秒,若期间收到了服务端回复的PING_ACK则表明连接还存在,否则直接关闭该连接。

public class NettyRWTimeoutClient {
    private static final int PORT = 8080;

    public static void main(String[] args) {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            // 30s内读写都空闲, 触发 IdleStateEvent
                            pipeline.addLast(new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS));
                            pipeline.addLast(new ClientHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect("localhost", PORT).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}

class ClientHandler extends SimpleChannelInboundHandler<String> {
    private final int maxPingAttempts = 3; // 最多发送3次PING
    private int pingCount = 0;
    private int pingTimeInterval = 10; // 每隔10s发送一次PING
    private ScheduledFuture<?> pingTask;

    @Override
    public void channelRead0(ChannelHandlerContext ctx, String msg) {
        if ("PING_ACK".equals(msg)) {
            System.out.println("Received PING_ACK from server");
            pingCount = 0; // PING_ACK, 重置Ping计数
            if (pingTask != null) {
                pingTask.cancel(false); // 取消后续PING任务
                pingTask = null;
            }
        } else {
            System.out.println("Received: " + msg);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.ALL_IDLE) {
                sendPingToServer(ctx);
            }
        }
    }

    private void sendPingToServer(ChannelHandlerContext ctx) {
        if (pingTask != null) {
            return; // 任务已在进行中
        }

        System.out.println("start send PINT To Server");
        pingTask = ctx.executor().scheduleAtFixedRate(() -> {
            if (pingCount < maxPingAttempts) {
                System.out.println("Sending PING " + (pingCount + 1));
                ctx.writeAndFlush("PING");
                pingCount++;
            } else {
                System.out.println("No response after " + maxPingAttempts + " PINGs, closing connection.");
                ctx.close();
                pingTask.cancel(false);
            }
        }, 0, pingTimeInterval, TimeUnit.SECONDS); // 每10s发送一次 PING
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

三.结语

上述博文在完成的过程中参考了如下资料:

  • Reactor Netty Reference Guide
  • Using Netty, understand its connection idle handling
  • Netty 官方文档——ReadTimeoutHandler

以上便是本文的全部内容,如果觉得不错可以支持一下博主,若有任何问题也敬请批评指正。

相关文章:

  • 【设计模式】建造者模式
  • Linux系统中查询命令行解释器
  • leetcode 75.颜色分类(荷兰国旗问题)
  • 基于 Docker 搭建 FRP 内网穿透开源项目
  • 2023南京理工大学计算机复试上机真题
  • .npy文件介绍
  • 网络协议栈
  • 农资出入库登记本,农药化肥库存出入库软件,佳易王农资管理庄稼医院开单管理系统操作教程
  • Java字节码
  • C++类与对象——拷贝构造与运算符重载
  • 【论文阅读】AlexNet——深度学习奠基作之一
  • 笔记本 Win10 部署阿里通义千问 1.5-0.5B 大模型 mini 版
  • nvm安装node失败的处理方法
  • hevc视频编码-搜索窗口和快速搜索
  • Project回调函数qsort②进阶应用
  • C++学习之路,从0到精通的征途:类和对象(中)
  • gdal-linux-whl文件安装下载地址
  • 常用的Python库
  • 【时延】空口资源计算
  • 5G核心网实训室搭建方案:轻量化部署与虚拟化实践
  • 建发股份:将于5月6日召开股东大会,审议提名林茂等为公司新一届董事等议案
  • 历史新高!上海机场一季度营收增至31.72亿元,净利润增34%
  • 老凤祥一季度净利减少两成,去年珠宝首饰营收下滑19%
  • 王星昊再胜连笑,夺得中国围棋天元赛冠军
  • 十四届全国人大常委会举行第四十三次委员长会议 ,听取有关草案和议案审议情况汇报
  • 龚正会见委内瑞拉副总统罗德里格斯