• Netty的InboundHandler 和OutboundHandler


    一、InboundHandler 和OutboundHandler的区别

    在Netty中,"inbound"表示来自外部来源(如网络连接)的数据,而"outbound"则表示从应用程序发起的IO操作。

    "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中以fireXXX命名的方法都是从IO流向业务handler的inboud事件

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

    总结:

    • InboundHandler处理从网络接收的数据,负责解析和处理输入数据。
    • OutboundHandler处理向网络发送的数据,负责封装和处理输出数据。
    • InboundHandler和OutboundHandler在处理数据的方向上有所区别,但它们通常一起使用,通过ChannelPipeline连接在一起,形成一个完整的数据处理链。
    • InboundHandler和OutboundHandler是Netty自身根据事件在Pipeline的流向抽象出来的术语,Mina框架没这个概念。

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

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

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

    1. import io.netty.bootstrap.ServerBootstrap;
    2. import io.netty.channel.*;
    3. import io.netty.channel.nio.NioEventLoopGroup;
    4. import io.netty.channel.socket.SocketChannel;
    5. import io.netty.channel.socket.nio.NioServerSocketChannel;
    6. public class EchoServer {
    7. private int port;
    8. public EchoServer(int port) {
    9. this.port = port;
    10. }
    11. public void start() throws Exception{
    12. EventLoopGroup boss = new NioEventLoopGroup();
    13. EventLoopGroup worker = new NioEventLoopGroup();
    14. ServerBootstrap bootstrap = new ServerBootstrap();
    15. bootstrap.group(boss, worker)
    16. .channel(NioServerSocketChannel.class)
    17. .childHandler(new ChannelInitializer() {
    18. @Override
    19. protected void initChannel(SocketChannel socketChannel) throws Exception {
    20. ChannelPipeline pipeline = socketChannel.pipeline();
    21. for (int i = 0; i < 3; i++) {
    22. pipeline.addLast(new InboundHandler(i + 1));
    23. }
    24. for (int i = 0; i < 3; i++) {
    25. pipeline.addLast(new OutboundHandler(i + 1));
    26. }
    27. System.out.println("handler注册顺序:");
    28. pipeline.names().forEach(x -> {
    29. System.out.println("handler:" + x);
    30. });
    31. }
    32. })
    33. .option(ChannelOption.SO_BACKLOG,100000)
    34. .childOption(ChannelOption.SO_KEEPALIVE,true);
    35. bootstrap.bind(port).sync();
    36. System.out.println("服务启动,监听端口@"+port);
    37. }
    38. public static void main(String[] args) throws Exception {
    39. new EchoServer(8001).start();
    40. }
    41. static class InboundHandler extends ChannelInboundHandlerAdapter {
    42. private int index;
    43. public InboundHandler(int index) {
    44. this.index = index;
    45. }
    46. @Override
    47. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    48. System.out.println("InboundChannel["+index+"]接收消息");
    49. ctx.fireChannelRead(msg);
    50. if (index == 3) {
    51. ctx.channel().write("df");
    52. }
    53. }
    54. }
    55. static class OutboundHandler extends ChannelOutboundHandlerAdapter {
    56. private int index;
    57. public OutboundHandler(int index) {
    58. this.index = index;
    59. }
    60. @Override
    61. public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    62. System.out.println("OutboundChannel["+index+"]发送消息");
    63. ctx.write(msg, promise);
    64. }
    65. }
    66. }

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

    2.2使用telnet发送数据

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

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

    2.3服务端响应

    从这个例子,可以看出

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

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

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

    1. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    2. System.out.println("InboundChannel["+index+"]接收消息");
    3. ctx.fireChannelRead(msg); //注释次代码,则事件不会往下个handler传递数据
    4. }
  • 相关阅读:
    Java开发的模板引擎--freemarker
    SpringACK对RabbitMQ消息的确认(消费)
    携程AI布局:三重创新引领旅游行业智能化升级
    剑指 Offer II 059. 数据流的第 K 大数值
    java中文件的输入和输出
    [GWCTF 2019]枯燥的抽奖
    【网络教程】SSH通过pem文件登陆(使用密钥对登陆)
    高效工作文档产出归类
    车牌识别流程
    ios打包,证书获取
  • 原文地址:https://blog.csdn.net/littleschemer/article/details/136419541