深刻Java网络编程与NIO(三)

Java NIO 与 Netty NIO

NIO的特性/NIO与IO区别:java

  • 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() 写入数据。app

ByteBuffer

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

Netty 中的 ByteBuf

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

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

2.Channels

channel

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

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) 会当即返回,而不会阻塞,固然,它只会做用一次。

相关文章
相关标签/搜索