详解java nio中的select和channel
什么是nio?
线程在处理数据时,如果线程还处于将数据从channel读到buffer的这段时间内,线程可以去做别的事情,等数据都读到buffer了,线程再回来处理读到的数据
channel是什么?
类比流的概念。与流的区别在于
1.channel是可读可写的,但是一个流要么写要么读
2.chanel可以异步的读和写
3.数据总是从channel中读到buffer,或者从buffer中写到channel
流的读取或写一般是一次性的操作,数据在读取过程中不会有缓存,这也就意味着没有办法自己随便移动到想要读取的位置,要实现这个功能也就只能先缓存
java中的channel有哪些?
filechannel:连接文件的channel,通过文件对象的getchannel方法即可获取
filechannel的write()方法不保证一次会写到channel中的字节数;另外它不能被设置为非阻塞,永远只能设置成阻塞模式
1.datagramchannel:处理udp协议连接,通过datagramchannel.open()然后再获取socket执行绑定即可端口
2.socketchannel:它是一个已经建立连接的tcp网络socket,用来处理tcp协议连接,通过socketchannel.open()再调用自身的connet即可建立
3.serversocketchannel:用来监听tcp连接的建立,通过serversocketchannel.open()可以建立,随后就可以绑定需要监听的端口,并等待连接的到来,每个已建立的连接都会返回一个socketchannel
非阻塞模式下,等待连接到来的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的连接建立、数据到达等等
实际上可以只用一个线程来管理所有的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,会立马返回
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。