首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

java网络编程之NIO(二)

NIO类库简介

在介绍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编程,我们首先要了解几个概念:

Buffer(缓冲区)、Channel (管道、通道)Selector(选择器、多路复用器)

下面我会一一介绍:

Buffer(缓冲区)

Buffer是一个对象,它包含一些要写入或者要读取的数据。在NIO类库中加入Buffer对象,体现了新库与原IO的一个重要的区别。在面向流的IO中,可以将数据直接写入或读取到Stream对象中。在NIO库中,所有数据都是用缓冲区处理的(读写)。缓冲区实质上是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用他类型的数组。这个数组为缓冲区提供了数据的访问读写等操作属性,如位置、容量、上限等概念,参考api文档。

Buffer类型:我们最常用的就是ByteBuffer,实际上每一种java基木类型都对于了一种缓存区(除了Boolean类型),如下所示:

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

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

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

下面有个小例子介绍Buffer类型使用。虽然不是很详细但是足够你了解使用了。

Channel (管道、通道)

通道(Channel),它就像自来水管道一样,网络数据通过Channel读取和写入,通道与流不同之处在于通道是双向的,而流只是一个方向上移动(一个流必须是Inputstream或OutputStream的子类),而通道可以用于读、写或者二者同时进行,最关键的是可以与多路复用器结合起来,有多种的状态位,方便多路复用器去识别。

事实上通道分为两大类,一类是网络读写的(SelectableChanneI),一类是用于文件操作的(FileChannel),我们使用MJSocketChanneI和ServerSockerChannel都是SelectableChannel的子类。

Selector(选择器、多路复用器)

多路复用器(Selector),他是NIO编程的基础,非常重要。多路复用器提供选择己经就绪的任务的能力。

简单说,就是Selctor会不断地轮询注册在其上的通道(Channel),如果某个通道发生了读写操作,这个通道就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以取得就绪的Channel集合,从而进行后续的IO操作。

一个多路复用器(Selector)可以负责成千上万Channel通道,没有上限,这也是JDK使用了epoll代了传统的select实现,获得连接句柄没有限制。这也就意味着我们只要一个线程负的轮询,就可以接入成千上万个客户端,这是JDK NIO库的巨大进步。

Selector线程就类似一个管理者(Master),管理了成千上万个管道,然后轮询那个管道的数据己经准备好,通知cpu执行IO的读取或写入操作。

Selector模式:当IO事件(管道)注册到选择器以后,Selector会分配给和个管道一个key值,相当于标签。Selector选择器是以轮询的方式进行查找注册的所有IO事件(管道),当我们的IO事件(管道)准备就绪后,select就会识别,会通过key值来找到相应的管道,进行相关的数据处理操作(从管道里读或写数据,写到我们的数据缓冲区中)。

每个管道都会对选择器进行注册不同的事件状态,以便选择器查找。

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRlTE

案例

下面每一行代码基本上都有注释,看不懂也正常api确实有点恶心特别是buffer。祝你好运,开启读代码模式。

server.java

Client.java

客户端运行结果:

客户端运行结果:

在客户端发一条信息服务端:

服务端接收到客户端的信息:

看完代码相信大家都会觉得NIO编程难度确实比同步阻塞BIO大很多,我们的NIO例程并没有考虑“半包读”和“半包写”,如果加上这些,代码将会更加复杂。NIO代码既然这么复杂,为什么它的应用却越来越广泛呢,使用NIO编程的优点总结如下:

1、客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像之前的客户端那样被同步阻塞;

2、 SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样IO通信线程就可以处理其它的链路,不需要同步等待这个链路可用;

3、 线程模型的优化:由于JDK的Selector在Linux等主流操作系统上通过epoll实现,它没有连接句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降,因此,它非常适合做高性能、高负载的网络服务器。

JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0,引人注目的是Java正式提供了异步文件IO操作,同时提供了与Unix网络编程事件驱动IO对应的AIO。下一章介绍AIO。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180207G13NH700?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券