【网络编程(三)】Netty入门到实战这一篇就够了

Netty

NIO 是一种 I/O 模型,netty 是基于 NIO 开发出来的一款异步事件驱动框架,它是一个通用的网络应用程序框架。netty 简化了 NIO 网络编程的开发,本质就是对NIO的封装和升级。它支持多种协议,如常用的协议有:HTTP(基于TCP的半双工协议)、WebSocket(支持全双工通信的协议,它一般被称为HTTP的进阶协议,它先是通过TCP三次握手建立连接后,然后通过HTTP握手转换成WebSocket协议,转换成功会发101状态码,之后就是WebSocket协议进行联系了,具体的可以看这篇(因为使用Netty的话使用WebSocket协议比较多):WebSocket协议:5分钟从入门到精通)。

Netty 核心组件和流程分析

  1. EventLoop(事件循环):它是一个循环,不断地等待事件的发生并处理这些事件。事件和NIO一样可以是网络连接的建立、数据的读取和写入、定时器的触发等等。一旦注册,将处理通道内的所有 I/O 操作。一个EventLoop实例通常会处理多个Channel,但这可能取决于实现细节和内部机制。
  2. EventLoopGroup(EventLoop的集合):它负责管理和调度多个 EventLoop 的创建和分配。可以理解它为线程池,然后其中每个线程都包含一个EventLoop。一般情况下,一个应用程序只需要一个 EventLoopGroup 来处理所有的网络事件。它俩的关系可以类比于 ThreadGroup和Thread之间的关系。内部封装了NIO中的Selector多路复用器。
  3. Channel(通道):与 java NIO 中的 SocketChannel 一样,可以进行数据的读取和写入。
  4. ChannelPipeline(通道管道):它由一系列的处理器(Handler)组成,用于处理数据的流动。ChannelPipeline 中的每个处理器叫做 ChannelHandler,这些ChannelHandler 负责处理不同的网络事件,例如数据的读取、写入和处理,连接的建立和关闭,异常的处理等等。**当数据进入管道的时候,它会依次经过 ChannelPipeline 中的每个 ChannelHandler 进行处理。**每个 ChannelHandler 可以对数据进行修改、转换或执行特定的业务逻辑。处理完毕会流通到下一个ChannelHandler。好似流水线。
  5. ChannelHanlder(通道处理器):用来处理 Channel 中的事件和数据的组件。使用的话是被封装到 ChannelPipeline 管道中。
    • ChannelInboundHandler:用于处理入站事件,例如连接建立、数据读取等。
    • ChannelOutboundHandler:用于处理出站事件,例如数据写入、连接关闭等。
    • SimpleChannelInboundHandler:继承自ChannelInboundHandler,简化了消息处理的逻辑。
    • SimpleChannelOutboundHandler:继承自ChannelOutboundHandler,简化了消息发送的逻辑。
    • HttpServerCodec:它负责处理 HTTP 请求和响应的编解码。
    • HttpObjectAggregator:将 HTTP 请求的多个部分合并成一个完整的 FullHttpRequest。
    • WebSocketServerProtocolHandler:处理 WebSocket 协议的握手和帧的编解码。
  6. ByteBuf(字节缓冲区):ByteBuf 是 Netty 中的字节容器,用于高效地存储和传输字节数据。与Java NIO 的 ByteBuffer 相比,ByteBuf 提供了更灵活的API和更高效的内存管理。
  7. Future(异步操作结果):Netty中的操作都是异步的,Future用来获取操作的状态和结果。
  8. Bootstrap(引导类):Bootstrap是启动客户端的类,负责配置和启动客户端的相关组件。
  9. ServerBootstrap(服务器引导类):ServerBootstrap是创建和启动服务器的类,用于配置和管理服务器的各个组件。其实是对NIO ServerSocketChannel 的封装。

注意:当你使用某工具调试的时候,服务端和网络调试工具并不是直接发送的。网路助手-》操作系统-》网络-》对方操作系统-》找到对应进程,是以这个路线发送数据和接收数据的,此过程都是0/1数据传输,在Netty中,使用的是 ByteBuf 进行字节的传输和存储。

下面是Netty的一个流程简图(这里的NioServerSocketChannel被构造时,会执行一个init方法,为这个channel中的Pipeline里设置handler,准备好去处理Selector选出的事件,大概流程就关联起来了):
在这里插入图片描述
这还有个 EventLoopGroup 封装多路复用器的一过程,且支持多线程处理(会向ServerBootstrap传递俩个EventLoopGroup,第一个称为BoosGroup用来处理新用户的连接请求,而第二个称为WorkerGroup,用来处理读写操作和业务逻辑也是为什么一般给BoosGroup线程数设置为1就够用的原因,因为它只处理连接请求):
在这里插入图片描述

数据流转的底层核心

在叙述数据流转底层核心时,先阐述 Netty 所提供的字符串入站、出站处理器。

从下面继承关系可以看见 StringDecoder 是 Netty 提供的入站处理器(继承了ChannelInBoundHandlerAdapter)

在这里插入图片描述

从下面的继承关系同样可以看出 StringEncoder 是 Netty 提供的出站处理器(继承了 ChannelOutBoundHandlerAdapter)

在这里插入图片描述

上述阐述过 ChannelPipeline 是用来封装 ChannelHandler 的管道,那它是怎么存储 ChannelHandler 的呢?其实底层是使用了双向链表进行存储的,至于为什么使用双向链表我觉得就三点:

  • 双向遍历(单链表无法完成):在服务端接收到事件的时候(接收到客户端来的信息时)会从头遍历到尾 ChannelHandler 进行事件处理;而在服务端向客户端回消息/发送消息时是从尾部遍历到头部 ChannelHandler 进行处理,双向链表是最佳选择。
  • 动态调整(单链表无法完成):单链表进行删除操作的话需要遍历整个链表,而无法动态的进行删除、添加。
  • 上下文可传递事件(单链表无法完成):使用双向链表的话,在一个节点上可以轻松的获取到其前一个和后一个节点,那在处理事件时,如果需要将一通道处理器的事件转换到下一个,就可以轻松的实现上下文传递。

拿下面配的 ChannelPipeline 进行数据流转说明

                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
   
    
    

                            Charset gbk = Charset.forName("GBK");
                            ch.pipeline().addLast("decoder",new StringDecoder(gbk));
                            ch.pipeline().addLast("encoder",new StringEncoder(gbk));
                            ch.pipeline().addLast(new DiscardServerHandler());// 将handler封装到Pipeline中
                        }

在这里插入图片描述

(这图有点问题哈,右边那个应该是双向链表的

猜你喜欢

转载自blog.csdn.net/qq_63691275/article/details/132241540
今日推荐