NIO/IO
一.IO
IO中的流只能是单向的
二.NIO的实现原理(jdk1.4)
NIO处理流程
(1)创建ServerSocketChannel, 配置它为非阻塞模式;
(2)绑定监听,配置TCP参数,例如backlog大小;
(3)创建一个独立的I/O线程,用于轮询多路复用器Selector;
(4)创建Selector, 将之前创建的ServerSocketChannel 注册到Selector. 上,监听
SelectionKey.ACCEPT;
(5)启动I/O线程,在循环体中执行Selector.select()方法, 轮询就绪的Channel;
(6)当轮询到了处于就绪状态的Channel时,需要对其进行判断,如果是OP_ACCEPT
状态,说明是新的客户端接入,则调用ServerSocketChannlaccept()方法接受新的客户端;
(7)设置新接入的客户端链路SocketChannel为非阻塞模式,配置其他的一些TCP参数;
(8)将SocketChannel注册到Selector, 监听OP_ READ操作位;
(9)如果轮询的Channel为OP_READ,则说明SocketChannel中有新的就绪的数据包
需要读取,则构造ByteBuffer对象,读取数据包;
(10)如果轮询的Channel为OP__WRITE,说明还有数据没有发送完成,需要继续发送990
以块的方式处理数据 ,Java NIO 由以下几个核心部分组成:
1)Channel
数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中 通道是可以双向的 。
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
2)Buffer
缓冲区:实质上是一个容器,一个连续的数组(通常指字节数组)。所有数据都通过 Buffer 对象来处理,写入到通道的所有对象都必须先放到缓冲区中;同样地,从通道中读取的任何数据都要先读到缓冲区中。
Buffer的核心参数
1.capacity 缓冲区数组的总长度
2.position 下一个要操作的数据元素的位置
3.limit 缓冲区数组中不可操作的下一个元素的位置,limit<=capacity
4.mark 用于记录当前 position 的前一个位置或者默认是 0
调用 mark() 时,它将记录当前 position 的前一个位置,当我们调用 reset 时,position 将恢复 mark 记录下来的值 。 当你把缓冲区的数局写入到管道中的时候,你需要调用flip()方法将Buffer从写模式切换到读模式,调用flip()方法会将position设回0,并将limit设置成之前position的值。
创建一个buffer------>存储数据(position)------->调用flip()( limit=position;position=0; )------->读取数据------->调用clear( position=0;而limit=capacity; )------->存储数据(循环)
NIO里实现的buffer类型
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
3)Selector
Selector.open() ----> SelectorProvider.provider() ----> WindowsSelectorProvider---->SelectorProviderImpl ---->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>不看了,这源码感觉看不完。也不知道到底想要啥.。。
Selector允许单线程处理多个 Channel。
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
NIO SelectionKey中定义的4种事件
1)SelectionKey.OP_ACCEPT —— 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了
2)SelectionKey.OP_CONNECT —— 连接就绪事件,表示客户与服务器的连接已经建立成功
3)SelectionKey.OP_READ —— 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)
- SelectionKey.OP_WRITE —— 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)
这里 注意,下面两种,SelectionKey.OP_READ ,SelectionKey.OP_WRITE ,
① 当向通道中注册SelectionKey.OP_READ事件后,如果客户端有向缓存中write数据,下次轮询时,则会 isReadable()=true;
② 当向通道中注册SelectionKey.OP_WRITE事件后,这时你会发现当前轮询线程中isWritable()一直为ture,如果不设置为其他事件
适用场景
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。