《Netty 权威指南》—— NIO类库简介

声明:本文是《Netty 权威指南》的样章,感谢博文视点授权并发编程网站发布样章,禁止以任何形式转载此文。

在介绍NIO编程之前,我们首先需要澄清一个概念,NIO到底是什么的简称?有人称之为New IO,因为它相对于之前的IO类库是新增的,所以被称为New IO,这是它的官方叫法。但是,由于之前老的IO类库是阻塞IO,New IO类库的目标就是要让JAVA支持非阻塞IO,所以,更多的人喜欢称之为非阻塞IO(Non-block IO),由于非阻塞IO更能够体现NIO的特点,所以本书使用的NIO都指的是非阻塞IO。

与Socket类和ServerSocket类相对应,NIO也提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用非常简单,但是性能和可靠性都不好,非阻塞模式正好相反。开发人员一般可以根据自己的需要来选择合适的模式,一般来说,低负载、低并发的应用程序可以选择同步阻塞IO以降低编程复杂度。但是对于高负载、高并发的网络应用,需要使用NIO的非阻塞模式进行开发。
下面的小节首先介绍NIO编程中的一些基本概念,然后通过NIO服务端的序列图和源码讲解,让大家快速的熟悉NIO编程的关键步骤和API的使用。如果你已经熟悉了NIO编程,可以跳过2.3章节继续学习后面的章节。

2.3.1. NIO类库简介

新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来同步阻塞I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就可以利用底层优化,这是原来的 I/O 包所无法做到的。下面我们对NIO的一些概念和功能做下简单介绍,以便大家能够快速的了解NIO类库和相关概念。

2.3.1.1. 缓冲区Buffer

我们首先介绍缓冲区(Buffer)的概念,Buffer 是一个对象, 它包含一些要写入或者要读出的数据。 在 NIO类库 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,我们将数据直接写入或者将数据直接读到 Stream 对象中。
在 NIO 库中,所有数据都是用缓冲区进行处理的。在读取数据时,它是直接读到缓冲区中;在写入数据时,它也是写入到缓冲区中。任何时候访问 NIO 中的数据,我们都是通过缓冲区进行读写操作。
缓冲区实质上是一个数组。通常它是一个字节数组(ByteBuffer),也可以使用其它种类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问,及维护读写位置(limit)等信息。
最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能用于操作byte数组。除了ByteBuffer,还有其它的一些缓冲区,事实上,每一种Java基本类型(除了Boolean类型)都对应有一种缓冲区,如下所示:

  • ByteBuffer:字节缓冲区
  • CharBuffer:字符缓冲区
  • ShortBuffer:短整型缓冲区
  • IntBuffer:整形缓冲区
  • LongBuffer:长整形缓冲区
  • FloatBuffer:浮点型缓冲区
  • DoubleBuffer:双精度浮点型缓冲区

缓冲区的类图继承关系如下所示:

3

Buffer继承关系图

每一个Buffer类都是Buffer接口的一个子实例。除了 ByteBuffer,每一个 Buffer 类都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数标准I/O操作都使用ByteBuffer,所以它除了具有一般缓冲区的操作之外还提供一些特有的操作,方便网络读写。

2.1.1.2. 通道Channel

Channel是一个通道,可以通过它读取和写入数据,它就像自来水管一样,网络数据通过Channel读取和写入。通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道可以用于读、写或者同时用于读写。

因为Channel是全双工的,所以它可以比流更好地映射底层操作系统的API。特别是在UNIX网络编程模型中,底层操作系统的通道都是全双工的,同时支持读写操作。

Channel的类图继承关系如下:

channel

Channel继承关系类图

自顶向下,前三层主要是Channel接口,用于定义它的功能,后面是一些具体的功能类(抽象类),从类图可以看出,实际上Channel可以分为两大类,分别是用于网络读写的SelectableChannel和用于文件操作的FileChannel。

本书涉及的ServerSocketChannel和SocketChannel都是SelectableChannel的子类,关于它们的具体用法将在后续的代码中体现。

2.1.1.3.多路复用器Selector

在本节中,我们将探索多路复用器Selector,它是JAVA NIO编程的基础,熟练的掌握Selector对于掌握NIO编程至关重要。多路复用器提供选择已经就绪的任务的能力。简单来讲,Selector会不断的轮询注册在其上的Channel,如果某个Channel上面有新的TCP连接接入、读和写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合进行后续的IO操作。

一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制。这也就意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端,这的确是一个巨大的改进。

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: 《Netty 权威指南》—— NIO类库简介

  • Trackback 关闭
  • 评论 (3)
  1. 请问,channelRead 接受到数据后,如何才能确保连接不被中断~

  2. 一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制。
    这样的说法不严谨吧!NIO涉及到底层的封装采用SPI的方式了提供了,不同平台提供了不同的provider,windows平台不支持epoll,只得使用select,JDK的作者为了突破最大文件描述符限制,在WindowsSelectorImpl内部采用多个线程select的方式,默认为8,每个负责1024个channel,当channel超出以后再继续加线程。所以在window使用nio不会出现最大文件描述符的限制。

    • miroku
    • 2014/05/16 12:34上午

    这个没有必要鸡蛋里挑骨头了,windows在服务器端的份额很少是其一,其二是多线程解决了文件描述符的问题,但是,也引入了多线程问题,说它扩展性低也勉强算对吧

return top