netty-daxin-2 (explanation of common events in netty)

Explanation of common events in netty

ChannelHandlerInterface

The processor interface is used to handle io events, intercept io operations, and pass the call to the next processor in the pipeline.

There are 2 sub-interfaces under it, and they all have corresponding adapter classes, and there is also a composite ChannelDuplexHandler class for handling inbound io events and outbound io operations.

  • ChannelInboundHandler: used to handle inbound io events
  • ChannelOutboundHandler: used to handle outbound io operations

context object

  • A ChannelHandler is bound to a ChannelHandlerContext context object, and the channelHandler should interact with the pipeline through the context object to which it is bound.
  • By using context objects, ChannelHandler can pass events to upstream or downstream processors, dynamically modify the pipeline, or use AttributeKey to store the handler's own data.

Status management

  • ChannelHandler often needs to store some status information. The simplest way is to define its own member variables in the handler class.
  • But since the handler has member variables, we have to create a new handler instance for each connection to avoid multi-thread concurrency problems.

UseAttributeKey

  • Although it is recommended to use member variables to store the state data of the handler itself, for some reasons, you may not want to create a new handler for each connection. In this case, you can use the AttributeKey provided by ChannelHandlerContext to solve this problem (as shown below), but currently ChannelHandlerContext#attr and ChannelHandlerContext#hasAttr are deprecated. It is recommended to use Channel#attr (AttributeKey) and Channel#hasAttr. This allows the same handler instance to be used in multiple pipelines.

    public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
          
          
    
        private final AttributeKey<Boolean> auth = AttributeKey.valueOf("auth");
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, Message msg) throws Exception {
          
          
    
            Attribute<Boolean> attr = ctx.attr(auth);
            if (msg instanceof LoginMessage) {
          
          
                authenticate((LoginMessage) msg);
                attr.set(true);
            } else if (message instanceof GetDataMessage) {
          
          
                if (Boolean.TRUE.equals(attr.get())) {
          
          
                    ctx.writeAndFlush(fetchSecret((GetDataMessage) msg));
                } else {
          
          
                    fail();
                }
            }
    
        }
    }
    

@Sharable annotation

  • If a ChannelHandler is marked with the @Sharable annotation, you only need to create the handler once and add it to different pipelines without any concurrency security issues.
  • If a ChannelHandler is not marked with the @Sharable annotation, a new handler must be created every time a handler is added to the pipeline because it has state.

ChannelHandlerAPI

  • As a top-level interface, ChannelHandler does not have many functions. It only provides three APIs:

    API describe
    handlerAdded() Called when the ChannelHandler is added to the ChannelPipeline
    handlerRemoved() Called when the ChannelHandler is removed from the ChannelPipeline
    exceptionCaught() Called when an exception occurs during ChannelHandler processing
  • From the API provided by ChannelHandler, we can see that it does not directly participate in the data processing process of Channel, but is used to respond to the ChannelPipeline chain and exception handling. The data processing of Channel is handled by its sub-interface:

public interface ChannelHandler {
    
    

    // 当ChannelHandler 添加到 ChannelPipeline 中时被调用
    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

    // 当 ChannelHandler 被从 ChannelPipeline 移除时调用
    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

   
    // 当 ChannelHandler 在处理过程中出现异常时调用
    @Deprecated
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

    
    @Inherited
    @Documented
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface Sharable {
    
    
        // no value
    }
}

ChannelHandler adapter class

public abstract class ChannelHandlerAdapter implements ChannelHandler {
    
    

   
    boolean added;
 
    protected void ensureNotSharable() {
    
    
        if (isSharable()) {
    
    
            throw new IllegalStateException("ChannelHandler " + getClass().getName() + " is not allowed to be shared");
        }
    }

   
    public boolean isSharable() {
    
    
       
        Class<?> clazz = getClass();
        Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
        Boolean sharable = cache.get(clazz);
        if (sharable == null) {
    
    
            sharable = clazz.isAnnotationPresent(Sharable.class);
            cache.put(clazz, sharable);
        }
        return sharable;
    }


    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    
    
        // NOOP
    }


    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    
    
        // NOOP
    }

  
    @Skip
    @Override
    @Deprecated
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
    
        ctx.fireExceptionCaught(cause);
    }
}

ChannelInboundHandler sub-interface

public interface ChannelInboundHandler extends ChannelHandler {
    
    

    // Channel 被注册到EventLoop 时
    void channelRegistered(ChannelHandlerContext ctx) throws Exception;

    // Channel 从 EventLoop 中取消时
    void channelUnregistered(ChannelHandlerContext ctx) throws Exception;

    // Channel 处于活跃状态,可以读写时
    void channelActive(ChannelHandlerContext ctx) throws Exception;

    // Channel 不再是活动状态且不再连接它的远程节点时
    void channelInactive(ChannelHandlerContext ctx) throws Exception;

    // Channel 读取数据时
    void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;

    // Channel 从上一个读操作完成时
    void channelReadComplete(ChannelHandlerContext ctx) throws Exception;

    // ChannelInboundHandler.fireUserEventTriggered()方法被调用时
    void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;

    // Channel 的可写状态发生改变时
    void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;

    
    @Override
    @SuppressWarnings("deprecation")
    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
}
Channel status

Source:Da Ming’s Netty column - Precision Data Craftsman: Exploring the mystery of Netty ChannelHandler

Channel is stateful, and Channel also provides an API to determine the current status of Channel, as follows:

  • isOpen(): Check whether the Channel is open.
  • isRegistered(): Check whether the Channel is registered.
  • isActive(): Check whether the Channel is active.
    The above three APIs correspond to the four states of Channel:
state describe
ChannelUnregistered The Channel has been created but has not yet been registered to the EventLoop. In this case isOpen() returns true, but isRegistered() returns false.
ChannelRegistered Channel has been registered with EventLoop. In this case isRegistered() returns true, but isActive() returns false.
ChannelActive The Channel is already active and can receive and send data. At this time isActive() returns true.
ChannelInactive Channel is not connected to the remote node

The status changes are as follows:
Insert image description here
When the status of Channel changes, corresponding events will be generated, and these events will be forwarded to ChannelHandler, and there will be corresponding methods in ChannelHandler to respond to it. Define some APIs related to this life cycle in ChannelHandler, such as channelRegistered(), channelUnregistered(), channelActive(), channelInactive(), etc. Brother Daming will introduce these APIs in detail later.

Calling time

The calling timing is as follows:
Insert image description here

ChannelHandler life cycle example
NetServer&CustomizeInboundHandler
@Slf4j
public class NettyServer11 {
    
    

    public static void main(String[] args) {
    
    
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup worker = new NioEventLoopGroup(16);

        ServerBootstrap serverBootstrap = new ServerBootstrap()
                .group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
    
    
                    @Override
                    protected void initChannel(SocketChannel ch) {
    
    
                        ch.pipeline().addLast(new CustomizeInboundHandler());
                    }
                });

        try {
    
    
            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8080)).sync();

            log.info("=======服务器启动成功=======");

            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
    
    
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }

}

@Slf4j
public class CustomizeInboundHandler implements ChannelInboundHandler {
    
    

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【handlerAdded】- handler 添加到 ChannelPipeline");
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelRegistered】- handler 注册到 eventLoop");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelActive】- Channel 准备就绪");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        log.info("【channelRead】- Channel 中有可读数据");
        if (msg instanceof ByteBuf) {
    
    
            try {
    
    
                ByteBuf byteBuf = (ByteBuf) msg;
                String code = byteBuf.toString(StandardCharsets.UTF_8);
                if ("evt".equals(code)) {
    
    
                    ctx.fireUserEventTriggered("JUST A EVT~");
                } else if ("ex".equals(code)) {
    
    
                    throw new NullPointerException("NULL POINTER~");
                } else if ("write".equals(code)) {
    
    
                    ByteBuf buf = ByteBufAllocator.DEFAULT.buffer().writeBytes("Great!Well Done~".getBytes());
                    ctx.channel().writeAndFlush(buf);
                } else {
    
    
                    log.info("服务端收到客户端发送的消息: {}", code);
                }
            } finally {
    
    
                ReferenceCountUtil.release(msg);
            }

        } else {
    
    
            ctx.fireChannelRead(msg);
        }

    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelReadComplete】- Channel 读取数据完成");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelInactive】- Channel 被关闭,不在活跃");
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelUnregistered】- Channel 从 EventLoop 中被取消");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【handlerRemoved】- handler 从 ChannelPipeline 中移除");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    
    
        log.info("【exceptionCaught】 - ChannelHandler处理发生异常");
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    
    
        log.info("【userEventTriggered】 - 激发自定义事件: {}", evt);
    }

    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
    
    
        log.info("【channelWritabilityChanged】 - 可写状态改变");
    }


}
NettyClient
@Slf4j
public class NettyClient11 {
    
    

    public static void main(String[] args) throws Exception {
    
    

        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        Channel channel = new Bootstrap()
                .group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
    
    
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
    
    
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
    
    
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
                                if (msg instanceof ByteBuf) {
    
    
                                    ByteBuf byteBuf = (ByteBuf) msg;
                                    log.info("客户端收到服务端发送的消息: {}", byteBuf.toString(StandardCharsets.UTF_8));
                                    ReferenceCountUtil.release(msg);
                                }
                            }
                        });
                    }
                })
                .connect("127.0.0.1", 8080)
                .sync()
                .channel();

        log.info("=======客户端连接服务器成功=======");

        Scanner sc = new Scanner(System.in);

        while (true) {
    
    

            System.out.print("输入:");

            String line = sc.nextLine();

            if (line == null || line.length() == 0) {
    
    
                continue;
            }

            if ("close".equals(line)) {
    
    
                channel.close().sync();
                eventLoopGroup.shutdownGracefully();
                break;
            }

            // 输入内容
            ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
            byteBuf.writeBytes(line.getBytes());

            channel.writeAndFlush(byteBuf);

            System.out.println("=======发送成功=======");
        }

    }

}

test

The client sends the following strings: evt, ex, write, halo, and close in sequence, and observe the log output
Server log

[15:57:54] [main] com.zzhua.test11.NettyServer11 [33] - =======服务器启动成功=======
[15:58:01] [nioEventLoopGroup-3-1] 【handlerAdded】- handler 添加到 ChannelPipeline
[15:58:01] [nioEventLoopGroup-3-1] 【channelRegistered】- handler 注册到 eventLoop
[15:58:01] [nioEventLoopGroup-3-1] 【channelActive】- Channel 准备就绪

[15:58:11] [nioEventLoopGroup-3-1] 【channelRead】- Channel 中有可读数据
[15:58:11] [nioEventLoopGroup-3-1] 【channelReadComplete】- Channel 读取数据完成

[15:58:22] [nioEventLoopGroup-3-1] 【channelRead】- Channel 中有可读数据
[15:58:22] [nioEventLoopGroup-3-1] 【exceptionCaught】 - ChannelHandler处理发生异常
[15:58:22] [nioEventLoopGroup-3-1] 【channelReadComplete】- Channel 读取数据完成

[15:58:31] [nioEventLoopGroup-3-1] 【channelRead】- Channel 中有可读数据
[15:58:31] [nioEventLoopGroup-3-1] 【channelReadComplete】- Channel 读取数据完成

[15:58:46] [nioEventLoopGroup-3-1] 【channelRead】- Channel 中有可读数据
[15:58:46] [nioEventLoopGroup-3-1]  服务端收到客户端发送的消息: halo
[15:58:46] [nioEventLoopGroup-3-1] 【channelReadComplete】- Channel 读取数据完成

[15:59:01] [nioEventLoopGroup-3-1] 【channelReadComplete】- Channel 读取数据完成
[15:59:01] [nioEventLoopGroup-3-1] 【channelInactive】- Channel 被关闭,不在活跃
[15:59:01] [nioEventLoopGroup-3-1] 【channelUnregistered】- Channel 从 EventLoop 中被取消
[15:59:01] [nioEventLoopGroup-3-1] 【handlerRemoved】- handler 从 ChannelPipeline 中移除

Client log

[15:58:01]  [main] com.zzhua.test11.NettyClient11 [48] - =======客户端连接服务器成功=======
输入:evt
=======发送成功=======
输入:ex
=======发送成功=======
输入:write
=======发送成功=======
输入:[15:58:31] [INFO ] [nioEventLoopGroup-2-1] com.zzhua.test11.NettyClient11 [37] - 客户端收到服务端发送的消息: Great!Well Done~
halo
=======发送成功=======
输入:close
Disconnected from the target VM, address: '127.0.0.1:65133', transport: 'socket'
analyze

Implementation process

  • After the server detects that the client initiates a connection, it will add the Handler to be processed to the ChannelPipeline, and then register the Channel to the EventLoop. After the registration is completed, the Channel is ready and active and can receive messages.
  • The client sends a message to the server, and the server reads the message
  • When the server detects that the client has closed the connection, the Channel is closed and no longer active. Then the Channel is canceled from the EventLoop and the Handler is removed from the ChannelPipeline.

Throughout the entire life cycle, the response method execution sequence is as follows:

  • Construction connection:handlerAdded() -> channelRegistered() -> channelActive ()
  • Number of reservations:channelRead() -> channelReadComplete()
  • Connection:channelReadComplete() -> channelInactive() -> channelUnregistered() -> handlerRemoved()

Here, Brother Daming makes a summary of the life cycle methods of ChannelHandler:

  • handlerAdded(): Triggered when ChannelHandler is added to Pipeline. When the server detects a new link, it will build the ChannelHandler into a two-way linked list (described in the next article). This method is triggered to indicate that a ChannelHandler business processing chain has been added to the current Channel.
  • channelRegistered(): Triggered when Channel is registered in EventLoop. This method is triggered, indicating that the current Channel has been bound to an EventLoop.
  • channelActive(): Triggered when the Channel connection is ready. This method is triggered, indicating that the current Channel is already active and can read and write data.
  • channelRead(): Triggered when the Channel has data to read. This method will be triggered when the client sends data to the server. When this method is called, it means there is data to read. And when we customize the business handler, we always override this method.
  • channelReadComplete(): Triggered when the Channel data has been read. This method will be triggered every time the server reads the data, indicating that the data has been read.
  • channelInactive(): Triggered when the Channel is disconnected. This method is triggered, indicating that the Channel is no longer active and the connection has been closed.
  • channelUnregistered(): Triggered when the Channel is unregistered: After the connection is closed, we need to cancel the binding relationship between the Channel and the EventLoop.
  • handlerRemoved(): Triggered when the ChannelHandler is removed from the ChannelPipeline. Remove all ChannelHandler business processing chains in the ChannelPipeline bound to the Channel.
ChannelInboundHandlerAdapter adapter class
public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
    
    

    @Skip
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelRegistered();
    }


    @Skip
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelUnregistered();
    }


    @Skip
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelActive();
    }


    @Skip
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelInactive();
    }


    @Skip
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        ctx.fireChannelRead(msg);
    }


    @Skip
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelReadComplete();
    }


    @Skip
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    
    
        ctx.fireUserEventTriggered(evt);
    }


    @Skip
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.fireChannelWritabilityChanged();
    }


    @Skip
    @Override
    @SuppressWarnings("deprecation")
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
    
    
        ctx.fireExceptionCaught(cause);
    }
}

SimpleChannelInboundHandler simple implementation
  • It helps us implement the ChannelInboundHandler interface. When using it, we only need to inherit it
  • Its generic representation means:
    • When it reads that the passed msg is of the type specified by the generic, it will pass the msg to the channelRead0 method and hand it over to the subclass for implementation, and after the call is completed, it will help us release the msg;
    • When the passed msg is not of the type specified by the generic, it will be passed directly to the next inbound processor. At this time, it will not help us release msg.
  • Benefit: It only processes messages of specified generic types, so you can avoid putting all the code for processing different types of messages in the same class
public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter {
    
    

    private final TypeParameterMatcher matcher;
    
    private final boolean autoRelease;


    protected SimpleChannelInboundHandler() {
    
    
        this(true);
    }


    protected SimpleChannelInboundHandler(boolean autoRelease) {
    
    
        matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
        this.autoRelease = autoRelease;
    }

 
    protected SimpleChannelInboundHandler(Class<? extends I> inboundMessageType) {
    
    
        this(inboundMessageType, true);
    }


    protected SimpleChannelInboundHandler(Class<? extends I> inboundMessageType, boolean autoRelease) {
    
    
        matcher = TypeParameterMatcher.get(inboundMessageType);
        this.autoRelease = autoRelease;
    }

    public boolean acceptInboundMessage(Object msg) throws Exception {
    
    
        return matcher.match(msg);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    
    
        boolean release = true;
        try {
    
    
            if (acceptInboundMessage(msg)) {
    
    
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg);
            } else {
    
    
                release = false;
                ctx.fireChannelRead(msg);
            }
        } finally {
    
    
            if (autoRelease && release) {
    
    
                ReferenceCountUtil.release(msg);
            }
        }
    }

    protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
}

When using ChannelInboundHandlerAdapter, it should be noted that we need to explicitly release the memory related to the pooled ByteBuf instance. Netty provides a special method for this purposeReferenceCountUtil.release(), that is, we need This method needs to be used at the end of the ChannelInboundHandler chain to release memory, as follows:

public class ByteBufReleaseHandler extends ChannelInboundHandlerAdapter{
    
    
  @Override
  public void channelRead(ChannelHandlerContext ctx,Object msg){
    
    
    //释放msg
    ReferenceCountUtil.release(msg);
  }
}

But some friends sometimes forget this and cause unnecessary trouble. Is there a better way? Netty provides a class to help us simplify this process: SimpleChannelInboundHandler. For our business processing classes, it can be solved by inheriting SimpleChannelInboundHandler instead of ChannelInboundHandlerAdapter.

Using SimpleChannelInboundHandler we don't need to explicitly release resources. Isn't it very user-friendly?

ChannelOutboundHandler sub-interface

public interface ChannelOutboundHandler extends ChannelHandler {
    
    
    
    // 请求将 Channel 绑定到本地地址时
    void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception;

    // 请求将 Channel 连接到远程节点时
    void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
                 SocketAddress localAddress, ChannelPromise promise) throws Exception;

    // 请求将 Channel 从远程节点断开时
    void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    // 请求关闭 Channel 时
    void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    // 请求将 Channel 从它的 EventLoop 注销时
    void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;

    // 请求从 Channel 中读取数据时
    void read(ChannelHandlerContext ctx) throws Exception;

    // 请求通过 Channel 将入队数据刷入远程节点时
    void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;

    // 请求通过 Channel 将数据写入远程节点时
    void flush(ChannelHandlerContext ctx) throws Exception;
}

ChannelOutboundHandlerAdapter adapter class
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {
    
    

   
    @Skip
    @Override
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
            ChannelPromise promise) throws Exception {
    
    
        ctx.bind(localAddress, promise);
    }

    
    @Skip
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception {
    
    
        ctx.connect(remoteAddress, localAddress, promise);
    }

    
    @Skip
    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
    
    
        ctx.disconnect(promise);
    }

   
    @Skip
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
    
    
        ctx.close(promise);
    }

    
    @Skip
    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
    
    
        ctx.deregister(promise);
    }

    
    @Skip
    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.read();
    }

   
    @Skip
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    
    
        ctx.write(msg, promise);
    }

   
    @Skip
    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.flush();
    }
}

ChannelDuplexHandler composite class

Inherits from ChannelInboundHandlerAdapter and implements the ChannelOutboundHandler interface.

public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler {
    
    

    
    @Skip
    @Override
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
                     ChannelPromise promise) throws Exception {
    
    
        ctx.bind(localAddress, promise);
    }

    
    @Skip
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
                        SocketAddress localAddress, ChannelPromise promise) throws Exception {
    
    
        ctx.connect(remoteAddress, localAddress, promise);
    }

    
    @Skip
    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
    
    
        ctx.disconnect(promise);
    }

   
    @Skip
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
    
    
        ctx.close(promise);
    }

    
    @Skip
    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
    
    
        ctx.deregister(promise);
    }

    
    @Skip
    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.read();
    }

   
    @Skip
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    
    
        ctx.write(msg, promise);
    }

    
    @Skip
    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
    
    
        ctx.flush();
    }
}

Guess you like

Origin blog.csdn.net/qq_16992475/article/details/135038855