• 深入Java网络编程与NIO(三)


    Java NIO 与 Netty NIO

    NIO的特性/NIO与IO区别:

    • 1)IO是面向流的,NIO是面向缓冲区的;
    • 2)IO流是阻塞的,NIO流是不阻塞的;
    • 3)NIO有选择器,而IO没有。

    读数据和写数据方式:

    • 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
    • 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。

    NIO三大核心组件:Channels 、Buffers 、Selectors
    Netty对应也有几大组件:

    Channel: 它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作;将会为每个channel分配一个EventLoop
    EventLoop: 控制流、多线程处理、并发
    EventLoopGroup
    ChannelHandler: 为了响应特定事件而被执行的回调: 每一个handler都在一个HanderPipeline中
    ChannelFuture: 异步通知

    netty

    1.Buffers

    buffer
    其实核心是最后的 ByteBuffer,前面的一大串类只是包装了一下它而已,我们使用最多的通常也是 ByteBuffer。
    MappedByteBuffer 用于实现直接内存映射mmp。

    Buffer 和数组差不多,它有 position、limit、capacity 几个重要属性。put() 一下数据、flip() 切换到读模式、然后用 get() 获取数据、clear() 一下清空数据、重新回到 put() 写入数据。

    ByteBuffer

    NIO的数据传输是基于缓冲区的,ByteBuffer正是NIO数据传输中所使用的缓冲区抽象。ByteBuffer支持在 堆外分配内存DirectBuffer,并且尝试避免在执行I/O操作中的多余复制。通过JNI调用来在堆外分配内存(调用malloc()函数在JVM堆外分配内存),这主要是为了避免额外的缓冲区复制操作。
    一般的I/O操作都需要进行系统调用,这样会先切换到内核态,内核态要先从文件读取数据到它的缓冲区,只有等数据准备完毕后,才会从内核态把数据写到用户态,所谓的阻塞IO其实就是说的在等待数据准备好的这段时间内进行阻塞。如果想要避免这个额外的内核操作,可以通过使用mmap(虚拟内存映射)的方式来让用户态直接操作文件。

    Netty 中的 ByteBuf

    网络传输的基本单位是字节,在Java NIO中提供了ByteBuffer作为字节缓冲区容器,但该类的API使用起来不太方便,所以Netty实现了ByteBuf作为其替代品,下面是使用ByteBuf的优点:

    相比ByteBuffer使用起来更加简单。
    通过内置的复合缓冲区类型实现了透明的zero-copy。
    容量可以按需增长。
    读和写使用了不同的索引指针。
    支持链式调用。
    支持引用计数与池化。
    可以被用户自定义的缓冲区类型扩展。

    2.Channels

    channel

    FileChannel:文件通道,用于文件的读和写
    DatagramChannel:用于 UDP 连接的接收和发送
    SocketChannel:把它理解为 TCP 连接通道,简单理解就是 TCP 客户端
    ServerSocketChannel:TCP 对应的服务端,用于监听某个端口进来的请求

    SocketChannel。它可以看作是 socket 的一个完善类,除了提供 Socket 的相关功能外,还提供了许多其他特性,如后面要讲到的向选择器注册的功能。

    Socket相关的类图:
    socket

    它类似于文件描述符,简单地来说它代表了一个实体(如一个硬件设备、文件、Socket或者一个能够执行一个或多个不同的I/O操作的程序组件)。你可以从一个Channel中读取数据到缓冲区,也可以将一个缓冲区中的数据写入到Channel。

    Netty中的 Channel

    • ChannelHandler
      ChannelHandler充当了处理入站和出站数据的应用程序逻辑的容器,该类是基于事件驱动的,它会响应相关的事件然后去调用其关联的回调函数,例如当一个新的连接被建立时,ChannelHandler的channelActive()方法将会被调用。

    Netty中到处都充满了 异步与事件驱动,而回调函数正是用于响应事件之后的操作。由于异步会直接返回一个结果,所以Netty提供了ChannelFuture(实现了java.util.concurrent.Future)来作为异步调用返回的占位符,真正的结果会在未来的某个时刻完成,到时候就可以通过ChannelFuture对其进行访问,每个Netty的出站I/O操作都将会返回一个ChannelFuture。

    3.Selectors

    Selector 建立在非阻塞的基础之上,大家经常听到的多路复用世界中指的就是它,用于实现一个线程管理多个 Channel。

    对于 Selector,我们还需要非常熟悉以下几个方法:

    1. select()

    调用此方法,会将上次 select 之后的准备好的 channel 对应的 SelectionKey 复制到 selected set 中。如果没有任何通道准备好,这个方法会阻塞,直到至少有一个通道准备好。

    2. electNow()

    功能和 select 一样,区别在于如果没有准备好的通道,那么此方法会立即返回 0。

    3. select(long timeout)

    看了前面两个,这个应该很好理解了,如果没有通道准备好,此方法会等待一会

    4. wakeup()

    这个方法是用来唤醒等待在 select() 和 select(timeout) 上的线程的。如果 wakeup() 先被调用,此时没有线程在 select 上阻塞,那么之后的一个 select() 或 select(timeout) 会立即返回,而不会阻塞,当然,它只会作用一次。

  • 相关阅读:
    python while循环语句 结合 if else pass temp语句求触发的余数 的练习题
    IF函数多个条件判断及嵌套
    Python 字符串 加减乘除
    Python条件语句 -- if ,else ( 如果 ,那么)
    input 变量名命名规则
    Python解释器的头部编码用途
    switch留个爪,之后还需要再研究下
    面向对象+JAVA基础
    爱因斯坦台阶
    成功的拆开了SELECT里JOIN个SELECT是啥
  • 原文地址:https://www.cnblogs.com/shawshawwan/p/10029678.html
Copyright © 2020-2023  润新知