Java NIO

Java NIO实例

Java基本套接字

基本操作

  • 连接远程机器。
  • 发送数据。
  • 接收数据。
  • 关闭连接。
  • 绑定端口。
  • 监听入站数据。
  • 在所绑定端口.上接受来自远程机器的连接。

其中,前四项用于客户端,后六项用于服务器,最后三项只有服务器才需要,即等待客
户端的连接,这些操作通过ServerSocket类实现。

Java NIO基础

概述

NIO解决一客户一线程所带来的开销大的问题
所谓一客户一线程是指,多个客户端访问同一个服务器进程时,服务器需要为每个请求创建一个服务线程

并且非堵塞是NIO实现的重要功能之一,为了实现非堵塞,NIO引入了

  • 选择器 Selector
  • 通道 Channel
    通道表示到实体(如硬件设备、文件、网络套接字或者可以执行一个或多个不同的的IO操作)的程序组件开放连接

通道可以注册一个选择器实例,通过该实例的select方法,用户可以询问“在一个或一组通道中,哪一个是当前需要的服务(即被读、写或被接受)“
在一个准备好的通道执行相应的I/O操作,就不需要等待,也就不会堵塞了

NIO与IO的区别

I0 NIO
面向流(Stream Oriented) 面向缓冲区(Buffer Oriented)
阻塞I0(Blocking I0) 非阻塞I0(Non Blocking I0)
(无) 选择器(Selectors)

缓冲区

NIO中一个主要的特性是java.nio.Buffer。缓冲区(Buffer)提供了一个比流抽象的、更高效和可预测的I/O。Buffer 代表了一个有限容量的容器一其本质是一个数组,通道Channel使用Buffer实例来传输数据
Buffer包含4个索引

  • capacity: 缓冲区总容量,可通过Buffer.capacity获取,并且是不可修改
  • position: 缓冲区位置,即下一个要写入或读取的索引,获取/设置通过position()/position(int)
  • limit: 缓冲区限制,即第一个不应该读取或写入的位置,获取/设置通过limit()/limit(int)
  • mark: 缓冲区位置标记,通过mark()设置一个位置,reset()方法被调用后,position被置为mark

    遵循如下规则

    0 ≤ mark ≤ position ≤ limit ≤ capacity

ByteBuffer的创建

1
2
3
4
5
6
7
//直接创建缓冲区
public static ByteBuffer allocate (int capacity)
//在某个字节数组上创建缓冲区
public static ByteBuffer wrap (byte[] array)
//上面的调用下面的
//上一个方法的返回 wrap(array, 0, array.length)
public static ByteBuffer wrap (byte[] array, int offset, int length)

这种方式与流的区别:流是单向的,而这种方式看读可写

ByteBuffer的读写

put()/get()方法:基于相对位置和绝对位置的读写
基于相对位置就是基于目前缓冲区位置position的当前值,从“下一个”位置读取或存放数据,并为position增加适当的值。

绝对位置的put)/get()方法,必须提供写入/读出的位置, 该方式的读写操作,不改变position的值

1
2
3
4
5
6
7
8
9
10
11
//相对位置
public byte get ()
public ByteBuffer get (byte[] dst)
public ByteBuffer get (byte[] dst, int offset,int length)
public ByteBuffer put (byte b)
public final ByteBuffer put (byte[] src)
public ByteBuffer put (byte[] src, int offset, int length)
public ByteBuffer put (ByteBuffer src)
//绝对位置
public byte get (int index)
public ByteBuffer put (int index, byte b)

需要注意的是 ,部分数据的get()/put()是不允许的。以写人
为例,如果要写入的数据量超过当前缓冲区允许写入的数据量(可通过Buffer.remaining()
方法获得该值),则所有的数据都不会写入缓冲区,position的位置不变,put()方法抛出
BufferOverflowException异常。


工具方法

  • clear() 通过clear()方法,缓冲区的poistion被设置为0, limit 设置为capacity,这样,缓冲区准备好接收新数据。后续的put()/readO调用,将数据从第-一个元素开始填入缓冲区,最多直到填满该缓冲区,达到limit位置(等于capacity)。

  • flip()方法用于将缓冲区准备为数据传出状态,该方法将limit设置为position后,将
    position 设置为0。后续的get()/write()方法将从缓冲区的第一个元素开始传出数据,直到
    limit位置。通过fip0方法和get(/write()方法配合,可以将前面利用put()/read)方法放入
    缓冲区的所有数据读出(一直读到limit)。

  • rewind)方法将position设置为0,但不改变limit的值,如果需要多次读取缓冲区里的
    数据,可以在两次读取间使用rewind()方法。

  • compact()方法将position和limit间的数据复制到缓冲区的开始位置,为后续的put()/
    read()调用让出空间。调用结束后,poistion的值被设置为数据的长度,也就是原来的limit
    减去position的值,而limit则设置为capacity。和clear()、fip() 等方法不同,compact() 不但改变了position 和limit的位置,还改变了缓冲区中的数据。

    compact()主要用于在缓冲区中还有未写出的数据时,为读入数据准备空间:即在write()方法调用后和添加新数据的read)方法前调用compact()方法,将未写出的“剩余”数据移动到缓冲区前面,为后面read()方法提供释放空间。在图中,假设write()方法调用后,缓冲区处于“某工作状态”,这时,position 到limit间的数据为未写出的“剩余”数据,而limit到capacity的空间则是read)方法可以使用的空间,通过compact()操作,position 到limit 间的数据被挪到缓冲区的前面,position 的位置也被设置为“剩余”数据长度。接下来,开发人员就可以直接调用read0/put0方法,从position位置开始放入数据。该数据和原来的“剩余”数据- -起,构成了连续的可用数据。
    Buffer还支持一些其他功能,如直接缓冲区(directbuffer)、Java基本类型的put()/get()、缓冲区共享、复制、透视、字符编码转换等

通道

一个Channel的实例代表一个 和设备的 连接

对象的创建

ServerSocketChannel/SocketChannel通过工厂方法创建

1
2
3
4
public static SocketChannel open() throws IOException
public static SocketChannel open(SocketAddress remote)
throws IOException
public static ServerSocketChannel open() throws IOException

SocketChannel创建后,可以通过connect()连接到远程机器,通过close()关闭连接,这
些操作和Socket的没有什么差别。

数据的读写

1
2
3
4
5
6
public int read (ByteBuffer dst)
public long read (ByteBuffer[] dsts, int offset, int length)
public final long read (ByteBuffer[] dsts)
public int write (ByteBuffer src)
public long write (ByteBuffer[] srcs, int offset, int length)
public final long write (ByteBuffer[] srcs)

和socket类似的操作,可以通过socket方法获取ServerSocket对象

1
2
public SocketChannel accept ()
public ServerSocket socket ()

是否支持工作在非阻塞状态

Buffer使用阻塞方法相对于基本套接字没有什么优点

1
2
public SelectableChannel configureBlocking (boolean block) //设置堵塞
public boolean isBlocking() //是否堵

非阻塞的SocketChannel 的connect()方法会立即返回,用户必须通过isConnected()判断连接是否已经建立,或者通过finishConnect()方法在非阻塞套接字上阻塞等待连接成功:非阻塞的read(),在Socket上没有数据的时候,立即返回(返回值为0),不会等待;非阻塞的acceptO,如果没有等待的连接,将返回null

1
2
public boolean isConnected ()
public boolean finishConnect ()

选择器

选择器(Selector) 的使用方法:通过静态的工厂方法创建Selector实例,通过Channel
的注册方法,将Selector实例注册到想要监控的Channel实例上,最后调用选择器的select()
方法。该方法会阻塞等待,直到有一个或多个通道准备好I/O操作或超时。select() 方法将返
回可进行I/O操作的通道数量。现在,在一个单独的线程中,就可以检查多个通道是否可以
进行I/O操作,不需要为每一一个通道都准备一个线程了。

选择器的打开关闭

1
2
3
public static Selector open ()
public boolean isOpen ()
public abstract void close ()

获取/设置标志位

选择器注册标记SelectionKey维护联的信息保存在java.nio.channels.SelectionKey实例中

  • OP_ READ (通道上有数据可读)
  • OP_ WRITE (通道已经可写)
  • OP_ CONNECT (通道连接已建立)
  • OP_ ACCEPT (通道上有连接请求)
1
2
public int interestOps ()
public SelectionKey interestops (int ops)

SeverSocketChannel/SocketChannel配合工作的API

1
2
3
4
public SelectionKey register (Selector sel, int ops) // 注册
public SelectionKey register (Selector sel, int ops, object att)//带附件注册
public SelectionKey keyFor (Selector sel) // 根据选择器,查找对应的selectionKey
public boolean isRegistered() // 判断通道是否已经注册

选择器实例selector.上注册了- -个SocketChannel对象,支持读操作

1
SelectionKey readKey = channel . register (selector, SelectionKey.OP_READ)

注销/获取选择器

1
2
3
Selector selector ()    //获取选择器
selectableChannel channel () //获取相应的channel
void cancel () //注销选择器

select方法

1
2
3
4
/**如果发现select()的返回值大于0,表明有需要处理的I/O事件发生**/
public int select()// 阻塞等待,直到一个注册通道有感兴趣的操作就绪
public int select (1ong timeout)// 等待一段时间,或一个注册通道有感兴趣的操作就绪
public int selectNow()// 非阻塞版本

获取相关建的方法

1
2
public Set<SelectionKey> keys () //selector上已注册的所有键
public Set <SelectionKey> selectedKeys()// 已选键集

Hadoop IPC上的使用

判断通道上等待操作的方法

1
2
3
4
5
public int readyOps ()
public boolean isReadable ()
public boolean iswritable ()
public boolean isConnectable ()
public boolean isAcceptable ()

与附件有关的另外两个方法

1
2
public Object attach (object ob)    //添加附件
public object attachment () //获取附件

以上内容来自《Hadoop技术内幕 深入解析HADOOP COMMON和HDFS架构设计与实现原理》


Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×