创建和001课程一致pipeline主要是处理器

handlerAdded
    ↓
channelRegistered  //代码没写
    ↓
channelActive
    ↓
channelRead            可能执行多次
    ↓
channelReadComplete    可能执行多次
    ↓
channelInactive
    ↓
channelUnregistered //代码没写
    ↓
handlerRemoved

代码

class LifecycleHandler extends SimpleChannelInboundHandler<String> {

    // 每个连接独有一个实例,所以这两个计数器是"这条连接"自己的计数
    private int readCount = 0;
    
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        // 触发时机:Handler 被加入 pipeline 时(通常是连接建立的极早期)
        System.out.println("① handlerAdded  -> 连接到来,装配 Handler,remote=" + ctx.channel().remoteAddress());
    }
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 触发时机:TCP 连接真正建立完成(每条连接只触发 1 次)
        System.out.println("② channelActive -> 连接已建立(只此一次)");
        // 连接建立后,可以主动给客户端发一句欢迎语
        ctx.writeAndFlush("欢迎连接 Echo 服务器!随便说点什么,我给你回声~\r\n");
    }
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        // 触发时机:每收到一条数据就调用一次(可触发 N 次)
        readCount++;
        System.out.println("③ channelRead   -> 第 " + readCount + " 次收到消息: " + msg);
        ctx.writeAndFlush("ECHO: " + msg + " (这是服务器回显)\r\n");
    }
    
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        // 触发时机:Netty 一次批量读取动作结束后触发(通常紧跟在若干 channelRead 之后)
        // 作用:把累积缓冲区里没刷出去的数据一次性刷到网络
        System.out.println("④ channelReadComplete -> 本轮读取完成,刷新缓冲区");
        ctx.flush();
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        // 触发时机:连接断开(客户端关闭 / 网络中断),每条连接只触发 1 次
        System.out.println("⑤ channelInactive -> 连接已断开(只此一次),共收到 " + readCount + " 条消息");
    }
    
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        // 触发时机:Handler 从 pipeline 中移除(连接销毁的收尾阶段)
        System.out.println("⑥ handlerRemoved  -> Handler 被移除,释放资源");
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 触发时机:链路中任何 Handler 抛出未捕获异常
        System.err.println("⚠ exceptionCaught -> 发生异常: " + cause.getMessage());
        // 一般做法:打印异常后关闭连接,避免脏连接
        ctx.close();
    }
}

客户端连接发送信息测试

① handlerAdded  -> 连接到来,装配 Handler,remote=/127.0.0.1:61014
② channelActive -> 连接已建立(只此一次)
③ channelRead   -> 第 1 次收到消息: 321
④ channelReadComplete -> 本轮读取完成,刷新缓冲区
④ channelReadComplete -> 本轮读取完成,刷新缓冲区
⑤ channelInactive -> 连接已断开(只此一次),共收到 1 条消息
⑥ handlerRemoved  -> Handler 被移除,释放资源

SimpleChannelInboundHandler

它提供两个便利:

  1. 自动筛选T消息。
  2. channelRead0()执行完成后自动释放接收到的消息。

channelRead0

客户端发送一次不保证服务端只触发一次channelRead0()。需要使用半包和粘包处理这个问题。