java nio中的select和channel是怎么使用的?

什么是NIO?线程在处理数据时,若是线程还处于将数据从channel读到buffer的这段时间内,线程能够去作别的事情,等数据都读到buffer了,线程再回来处理读到的数据html

channel是什么?

channel表明对实体的一个链接,实体包括文件、网络socket等一些能进行I/O操做(读、写)的设备。类比流的概念。与流的区别在于java

  1. channel是可读可写的,可是一个流要么写要么读数组

  2. chanel能够异步的读和写缓存

  3. 数据老是从channel中读到buffer,或者从buffer中写到channelbash

流的读取或写通常是一次性的操做,数据在读取过程当中不会有缓存,这也就意味着没有办法本身随便移动到想要读取的位置,要实现这个功能也就只能先缓存网络

java中的channel有哪些?

  1. FileChannel:链接文件的channel,经过文件对象的getChannel方法便可获取

FileChannel的write()方法不保证一次会写到channel中的字节数;另外它不能被设置为非阻塞,永远只能设置成阻塞模式异步

  1. DatagramChannel:处理UDP协议链接,经过DatagramChannel.open()而后再获取socket执行绑定便可端口socket

  2. SocketChannel:它是一个已经创建链接的TCP网络socket,用来处理TCP协议链接,经过SocketChannel.open()再调用自身的connet便可创建ui

  3. ServerSocketChannel:用来监听TCP链接的创建,经过ServerSocketChannel.open()能够创建,随后就能够绑定须要监听的端口,并等待链接的到来,每一个已创建的链接都会返回一个SocketChannelspa

非阻塞模式下,等待链接到来的accept方法会立马返回,注意判断SocketChannel是否是null;另外可能有多个链接创建,因此监听通常会放在一个while循环里面

Buffer是什么?

用来方便操做内存块中数据的一个包装类。它有3个属性

  1. capacity:表示Buffer能容纳的数据量,满了就不能再写

  2. position:读或者写开始的位置

  3. limit:写模式下表示能往buffer中写的数据量,最大值是capacity;读模式下表示能从buffer中获取的数据量,以前buffer中写了多少,就能读多少

从写模式转换到读模式须要用flip()完成,调用完成以后,limit会被设置成position当时的值,而positon会被设置成0;

读取数据完毕转换成写须要调用clear或者compact方法,其中clear会置position为0,limit为capacity,compact则会把原有的数据拷贝到开始的位置,而后其后的位置设置为position,limit则是capacity

mark和reset用法:在执行读取的时候,先mark住当前的位置,执行读取完成以后reset就回到原读取数据以前的位置了

怎么读取数据到多个Buffer?

建立一个数组用来放要写的数据,或者将要读到的数据,再执行读写操做便可,可是这种方式不适合读取变长消息

Buffer[] bArr = {head,body};
channel.read(bArr); //读 ,若是head自己会放自身容量的数据而后再往body中塞
Buffer[] wArr={head,body}
channel.write(wArr);//写
复制代码

什么是多路复用?

在网络中,多路复用是指将多个模拟信号或者数字信号组合成一种信号的方法,以便可以在共享媒介上传输。它的目标是共享稀缺资源,好比历史上多个固定电话信号都是经过一根电线来通话。

多路复用的信号经过通讯通道好比电缆来传播,多路复用器将通讯通道的容量划分红几个逻辑通道,每个通道对应要传输的信号或者数据流,接收方则经过解复用来提取对应的原始信号

内容来之维基百科

Selector是干啥的?

用来监控多个channel的事件,好比channel的链接创建、数据到达等等。

Selector是SelectableChannel的多路复用器,针对不一样的操做系统有不一样的实现,好比PollSelectorImpl和EpollSelector,固然也能够自定义实现。

使用SelectionKey来表示一个SelectableChannel用Selector注册了,在Selector内部会维护三种selection key的集合

  • key set表示使用了本Selector的注册的channel,经过 keys()方法返回

  • selected-key set表示对应channel有channel自身"感兴趣"的事件发生的Selectionkey集合,经过 selectedKeys()返回

  • cancelled-key set表示selector已经执行了cancel可是还还没有完成解绑流程的channel,它是没法直接获取的

新建Selector的时候是这三个集合都是空的

实际上能够只用一个线程来管理全部的channel

selector使用示例

//建立selector
Selector selector = Selector.open();
//使用Selector必须设置为false,同时意味着FileChannel是不能用Selector
channel.configureBlocking(false);
// SelectionKey一共有4种值,分别表明4个事件:connect、accept、read、write
// 经过方法 interestOps 能够获得注册时对channel感兴趣的事件,具体获取方式为 interestSet & SelectionKey.OP_ACCEPT 获得的结果便是否为ACCEPT事件
//经过这种方式即实现了注册,代表当前channel须要监听的是 read 事件,若是对多个事件感兴趣,那么可使用 SelectionKey.OP_READ | SelectionKey.OP_WRITE 方式实现
//注册方法还能够添加另外一个参数,attach,用来附加更多的信息给channel,好比将Buffer给channel
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
while(true) {
  //select()对channel注册的事件若是一个都没有好,那么阻塞住,返回值表示事件已经发生的chanel的个数;
  //selectNow()则不阻塞,没有准备好就返回0
  int readyChannels = selector.select();
  if(readyChannels == 0) continue;
  //用来获取准备好的channel
  Set<SelectionKey> selectedKeys = selector.selectedKeys();
  Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
  while(keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if(key.isAcceptable()) {
      //SeverSocketChannel接受了一个新的链接
    } else if (key.isConnectable()) {
      //和远程已经创建了链接
    } else if (key.isReadable()) {
      //channel可读
    } else if (key.isWritable()) {
      //channel可写
    }
    //必须手动执行
    keyIterator.remove();
  }
}
复制代码

wakeup:若是channel当前恰好阻塞在select,会立马返回

java NIO 参考

相关文章
相关标签/搜索