什么是NIO?线程在处理数据时,如果线程还处于将数据从channel读到buffer的这段时间内,线程可以去做别的事情,等数据都读到buffer了,线程再回来处理读到的数据
channel代表对实体的一个连接,实体包括文件、网络socket等一些能进行I/O操作(读、写)的设备。类比流的概念。与流的区别在于
流的读取或写一般是一次性的操作,数据在读取过程中不会有缓存,这也就意味着没有办法自己随便移动到想要读取的位置,要实现这个功能也就只能先缓存
FileChannel的write()方法不保证一次会写到channel中的字节数;另外它不能被设置为非阻塞,永远只能设置成阻塞模式
非阻塞模式下,等待连接到来的accept方法会立马返回,注意判断SocketChannel是不是null;另外可能有多个连接建立,所以监听一般会放在一个while循环里面
用来方便操作内存块中数据的一个包装类。它有3个属性
从写模式转换到读模式需要用flip()完成,调用完成之后,limit会被设置成position当时的值,而positon会被设置成0;
读取数据完毕转换成写需要调用clear或者compact方法,其中clear会置position为0,limit为capacity,compact则会把原有的数据拷贝到开始的位置,然后其后的位置设置为position,limit则是capacity
mark和reset用法:在执行读取的时候,先mark住当前的位置,执行读取完成之后reset就回到原读取数据之前的位置了
创建一个数组用来放要写的数据,或者将要读到的数据,再执行读写操作即可,但是这种方式不适合读取变长消息
Buffer[] bArr = {head,body};
channel.read(bArr); //读 ,如果head本身会放自身容量的数据然后再往body中塞
Buffer[] wArr={head,body}
channel.write(wArr);//写
复制代码
在网络中,多路复用是指将多个模拟信号或者数字信号组合成一种信号的方法,以便能够在共享媒介上传输。它的目标是共享稀缺资源,比如历史上多个固定电话信号都是通过一根电线来通话。
多路复用的信号通过通信通道比如电缆来传播,多路复用器将通信通道的容量划分成几个逻辑通道,每一个通道对应要传输的信号或者数据流,接收方则通过解复用来提取对应的原始信号
用来监控多个channel的事件,比如channel的连接建立、数据到达等等。
Selector是SelectableChannel的多路复用器,针对不同的操作系统有不同的实现,比如PollSelectorImpl和EpollSelector,当然也可以自定义实现。
使用SelectionKey来表示一个SelectableChannel用Selector注册了,在Selector内部会维护三种selection key的集合
新建Selector的时候是这三个集合都是空的
实际上可以只用一个线程来管理所有的channel
//创建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 参考