Netty的InboundHandler 和OutboundHandler

一、InboundHandler 和OutboundHandler的区别

在Netty中,"inbound"表示来自外部来源(如网络连接)的数据,而"outbound"则表示从应用程序发送到外部目标(如网络连接或其他服务)的数据。

"Inbound"主要涉及应用程序接收和处理外部数据的过程。这包括从网络连接读取数据、解码处理数据、执行业务逻辑等操作。例如,在一个服务器应用程序中,inbound操作可能涉及监听和接受客户端连接,读取客户端发送的请求数据,并将其转发给适当的处理程序进行处理。inbound监听的事件如下:

  • ++channelRegistered/channelUnregistered++
  • ++channelActive/channelInactive++
  • ++channelRead++
  • ++channelReadComplete++
  • ++channelWritabilityChanged++
  • ++userEventTriggered++
  • ++exceptionCaught++

"Outbound"主要涉及应用程序发送数据到外部目标的过程。这包括将数据编码为网络传输的格式、通过网络连接发送数据、处理发送的数据等操作。例如,在一个客户端应用程序中,outbound操作可能涉及将请求数据编码为适当的传输协议,通过网络连接发送给服务器,并等待响应数据。outbound监听的事件如下:

  • ++bind++
  • ++connect++
  • ++disconnect++
  • ++close++
  • ++deregister++
  • ++read++
  • ++write++
  • ++flush++

Netty的Pipeline采用责任链设计模式,责任链的每个节点是ChannelHandlerContext,通过prev和next节点实现双向链表。ChannelHandlerContext对象封装了ChannelHandler对象。Pipeline的首尾节点分别是io.netty.channel.DefaultChannelPipeline.HeadContext和io.netty.channel.DefaultChannelPipeline.TailContext。

Pipeline在分发事件的时候,会根据事件类型选择合适的handler(Inbound/Outbound本身会通用掩码位注册监听的事件类型),可参考 《Netty之ChannelHandlerMask详解》

总结:

  • InboundHandler处理从网络接收的数据,负责解析和处理输入数据。
  • OutboundHandler处理向网络发送的数据,负责封装和处理输出数据。
  • InboundHandler和OutboundHandler在处理数据的方向上有所区别,但它们通常一起使用,通过ChannelPipeline连接在一起,形成一个完整的数据处理链。

二、客户端消息在handler的流向

我们通过简单的例子来说明

2.1注册一个简单的Netty服务器

java 复制代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class EchoServer {

    private int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws Exception{
        EventLoopGroup boss = new NioEventLoopGroup();
        EventLoopGroup worker = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        for (int i = 0; i < 3; i++) {
                            pipeline.addLast(new InboundHandler(i + 1));
                        }
                        for (int i = 0; i < 3; i++) {
                            pipeline.addLast(new OutboundHandler(i + 1));
                        }
                        System.out.println("handler注册顺序:");
                        pipeline.names().forEach(x -> {
                            System.out.println("handler:" + x);
                        });
                    }
                })
                .option(ChannelOption.SO_BACKLOG,100000)
                .childOption(ChannelOption.SO_KEEPALIVE,true);
        bootstrap.bind(port).sync();
        System.out.println("服务启动,监听端口@"+port);

    }

    public static void main(String[] args)  throws Exception {
        new EchoServer(8001).start();
    }

    static class InboundHandler extends ChannelInboundHandlerAdapter {

        private int index;

        public InboundHandler(int index) {
            this.index = index;
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("InboundChannel["+index+"]接收消息");
            ctx.fireChannelRead(msg);
            if (index == 3) {
                ctx.channel().write("df");
            }
        }

    }

    static class OutboundHandler extends ChannelOutboundHandlerAdapter {

        private int index;

        public OutboundHandler(int index) {
            this.index = index;
        }


        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            System.out.println("OutboundChannel["+index+"]发送消息");
            ctx.write(msg, promise);
        }
    }

}

在这个例子,启动一个简单的socket服务器,并依次注册了三个inboundhandler,三个outboundhandler。启动之后,通过简单的telnet命令,往socket发送数据

2.2使用telnet发送数据

使用windows自带的telnet,输入命令:telnet localhost 8001,然后随便输入一个字符

(部分系统需要通过按"Win +R"快捷键,打开"运行"对话框,输入"optionalfeatures"后按回车键;在打开的"Win dows功能"窗口中,找到并勾选"Telnet客户端")

2.3服务端响应

从这个例子,可以看出

InboundHandler是按照Pipleline的加载顺序,顺序执行;

而OutboundHandler是按照Pipleline的加载顺序,逆序执行。

需要注意的是,由于pipeline采取的是责任链方式,在任何一个节点,如果没有把事件往下传,事件就会在本节点终止。

java 复制代码
   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            System.out.println("InboundChannel["+index+"]接收消息");
            ctx.fireChannelRead(msg);  //注释次代码,则事件不会往下个handler传递数据
        
   }
相关推荐
Y_3_79 小时前
Netty实战:从核心组件到多协议实现(超详细注释,udp,tcp,websocket,http完整demo)
linux·运维·后端·ubuntu·netty
安徽杰杰7 天前
能源即服务:智慧移动充电桩的供给模式创新
netty
安徽杰杰8 天前
新基建浪潮下:中国新能源汽车充电桩智慧化建设与管理实践
netty
迢迢星万里灬9 天前
Java求职者面试:微服务技术与源码原理深度解析
java·spring cloud·微服务·dubbo·netty·分布式系统
触角云科技10 天前
掌上充电站:基于APP/小程序的新能源汽车智慧充电管理
netty
安徽杰杰10 天前
智慧充电:新能源汽车智慧充电桩的发展前景受哪些因素影响?
netty
漫步者TZ14 天前
【Netty系列】解决TCP粘包和拆包:LengthFieldBasedFrameDecoder
java·网络协议·tcp/ip·netty
安徽杰杰15 天前
智慧赋能:移动充电桩的能源供给革命与便捷服务升级
netty
安徽杰杰15 天前
智慧赋能:新能源汽车充电桩应用现状与管理升级方案
netty
触角云科技19 天前
智慧充电桩数字化管理平台:环境监测与动态数据可视化技术有哪些作用?
netty