NIO对文件的简易读写操作以及说明
2018-01-01 14:47
225 查看
首先了解一下NIO中的重要概念 通道、缓冲区、选择器
通道- 类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer
或者 从一个buffer写入,即通道与buffer进行数据交互。
通道类型:
o FileChannel:从文件中读写数据的通道。通过使用一个InputStream、Out
4000
putStream或RandomAccessFile来获取一个FileChannel实例,FileChannel比较特殊,它可以与通道进行数据交互,
不能切换到非阻塞模式,套接字通道可以切换到非阻塞模式。
o DatagramChannel:能通过UDP读写网络中的数据的通道,直接通过DatagramChannel.open()获取一个DatagramChannel实例。
o SocketChannel:能通过TCP读写网络中的数据的通道,直接通过SocketChannel.open()获取一个SocketChannel实例。
o ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel,直接通过ServerSocketChannel.open()获取一个ServerSocketChannel实例。
缓冲区-
本质上是一块可以存储数据的内存,被封装成了buffer对象!
缓冲区类型:
o ByteBuffer
o MappedByteBuffer
o CharBuffer
o DoubleBuffer
o FloatBuffer
o IntBuffer
o LongBuffer
o ShortBuffer
缓冲区常用方法:
o allocate() -
分配一块缓冲区,可以指定缓冲区大小
o put() - 向缓冲区写数据
o get() -
向缓冲区读数据
o array() -
返回缓冲区内容的字节数组
o filp() -
翻转缓冲区,读写模式进行翻转
o clear() -
从写模式切换到读模式,不会清空数据,如果想循环将缓冲区中的数据提取出来进行输出,不进行clear()和filp()的话,将会出现死循环,如果是在一个循环内filp()放置在clear()的后面,也会出现死循环,使用时根据实际情况进行设置。
o compact() -
从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
o mark() -
对position做出标记,配合reset使用
o reset() -
将position置为标记值
缓冲区的一些属性:
o capacity -
缓冲区大小,无论是读模式还是写模式,为激活时设定大小,此属性值不会变;
o position -
写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;
最大为capacity - 1;切换到读模式时,position会被置为0,表示当前读的位置从哪开始
o limit -
写模式下,limit
相当于capacity
表示最多可以向缓冲区写多少数据,切换到读模式时,
limit
等于原先的position,表示最多可以从缓冲区读多少数据。
下面是一个File的读写示例:case.txt中内容为this is test
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(100);
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
System.out.println("读取文件内容:"+new String(buffer.array(),0,bytes,"utf-8"));
}
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:100 容量是:100 位置为:0
字节数:15
写入缓冲区后限制数:100 容量是:100 位置为:15
读取文件内容:this is test
从以上这些结果中,看似正常,实际在应用中,我们的文件中的内容不可能只有100个字节以内,所以一旦超出100个字节,上面示例将会出现死循环,下面就针对这个问题进行修改:
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
StringBuilder builder = new StringBuilder();
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//使用clear获或者是flip
buffer.clear();//这里是将缓冲区从写模式转换为读模式,因为下面要将数据从缓冲区中将数据拿出来打印
// buffer.flip();//这里是对缓冲区进行翻转,如果这里两个方法一起用也将导致死循环
System.out.println("翻转缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
builder.append(new String(buffer.array(),0,bytes,"utf-8"));
}
System.out.println("读取文件内容:"+builder.toString());
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:10 容量是:10 位置为:0
字节数:10
写入缓冲区后限制数:10 容量是:10 位置为:10
翻转缓冲区后限制数:10 容量是:10 位置为:0
字节数:5
写入缓冲区后限制数:10 容量是:10 位置为:5
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容:this is test
以上修改后的代码,我将缓冲区容量改小了,文件中内容没变,从结果可以看出从通道写入缓冲区两次,写入缓冲区后pos位置是不一样的,是根据字节数的变化而变化,filp后或者是clear后,pos位置是都回到了初始化的位置。你可以认为是在filp或者clear后,你不指定起止位置的情况下,从缓冲区提取数据都是从0位置开始取到limit位置,包含空闲的缓冲区空间,而我这里是因为设置了起止值new
String(buffer.array(),0,bytes,"utf-8")。下面我们再来验证一下clear或者filp后,原缓冲区的数据是否还存在?
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//使用clear获或者是flip
buffer.clear();//这里是将缓冲区从写模式转换为读模式,因为下面要将数据从缓冲区中将数据拿出来打印
// buffer.flip();//这里是对缓冲区进行翻转
System.out.println("翻转缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
System.out.println("读取文件内容:"+new String(buffer.array(),"utf-8"));
}
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:10 容量是:10 位置为:0
字节数:10
写入缓冲区后限制数:10 容量是:10 位置为:10
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容:this is
字节数:5
写入缓冲区后限制数:10 容量是:10 位置为:5
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容: testis is //这里很明显的内容不对
通过以上结果可以看出,当你在缓冲区提取数据时,如果没有指定提取的起止位置,那么后面提取到的内容并不是我们想要的。也就是说我们在调用clear方法或者flip方法后,再次从通道中去写入数据到缓冲区,缓冲区中原有的数据是不会进行清除的,会直接根据本次所需空间进行覆盖,上面输出结果第二次读取5个字节写入到缓冲区,因为缓冲区最大容量10个字节.所以就会覆盖前5个字节,还保留着第一次缓冲区中的后5个字节内容,这样结合起来的数据就完全变了样。
一个从A文件到B文件的NIO读写例子:case.txt中内容不变
public static void writFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
File f2= new File("C:\\Users\\zht\\Desktop\\case2.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
FileOutputStream out = new FileOutputStream(f2);
FileChannel channel2 =out.getChannel();
System.out.println("缓冲区输入前限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
int bytes = -1;
while((bytes = channel.read(buffer)) != -1){
System.out.println("缓冲区输入后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
ByteBuffer buffer2 = Charset.forName("utf-8").encode(new String(buffer.array(),0,bytes,"utf-8"));
while(buffer2.hasRemaining()){
channel2.write(buffer2);
System.out.println("缓冲区输出后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
}
buffer.flip();
System.out.println("缓冲区翻转后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
}
out.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
选择器:相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道。
通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:
o SelectionKey.OP_CONNECT
o SelectionKey.OP_ACCEPT
o SelectionKey.OP_READ
o SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
通道向选择器注册时,会返回一个 SelectionKey对象,具有如下属性
o interest集合
o ready集合
o Channel
o Selector
o 附加的对象(可选)
用“位与”操作interest 集合和给定的SelectionKey常量,可以确定某个确定的事件是否在interest 集合中。
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
ready 集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set。Selection将在下一小节进行解释。
可以这样访问ready集合:
int readySet = selectionKey.readyOps();
可以使用以下四个方法获取已就绪事件,返回值为boolean:
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
可以将一个对象或者更多信息附着到SelectionKey上,即记录在附加对象上,方法如下:
selectionKey.attach(theObject);
Object attachedObj
= selectionKey.attachment();
可以通过选择器的select方法获取是否有就绪的通道,返回值表示上次执行select之后,就绪通道的个数。
int select();
int select(long timeout);
int selectNow();
可以通过selectedKeySet获取已就绪的通道。返回值是SelectionKey 的集合,处理完相应的通道之后,需要removed
因为Selector不会自己removed
select阻塞后,可以用wakeup唤醒;执行wakeup时,如果没有阻塞的select那么执行完wakeup后下一个执行select就会立即返回。
调用close() 方法关闭selector
通道- 类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer
或者 从一个buffer写入,即通道与buffer进行数据交互。
通道类型:
o FileChannel:从文件中读写数据的通道。通过使用一个InputStream、Out
4000
putStream或RandomAccessFile来获取一个FileChannel实例,FileChannel比较特殊,它可以与通道进行数据交互,
不能切换到非阻塞模式,套接字通道可以切换到非阻塞模式。
o DatagramChannel:能通过UDP读写网络中的数据的通道,直接通过DatagramChannel.open()获取一个DatagramChannel实例。
o SocketChannel:能通过TCP读写网络中的数据的通道,直接通过SocketChannel.open()获取一个SocketChannel实例。
o ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel,直接通过ServerSocketChannel.open()获取一个ServerSocketChannel实例。
缓冲区-
本质上是一块可以存储数据的内存,被封装成了buffer对象!
缓冲区类型:
o ByteBuffer
o MappedByteBuffer
o CharBuffer
o DoubleBuffer
o FloatBuffer
o IntBuffer
o LongBuffer
o ShortBuffer
缓冲区常用方法:
o allocate() -
分配一块缓冲区,可以指定缓冲区大小
o put() - 向缓冲区写数据
o get() -
向缓冲区读数据
o array() -
返回缓冲区内容的字节数组
o filp() -
翻转缓冲区,读写模式进行翻转
o clear() -
从写模式切换到读模式,不会清空数据,如果想循环将缓冲区中的数据提取出来进行输出,不进行clear()和filp()的话,将会出现死循环,如果是在一个循环内filp()放置在clear()的后面,也会出现死循环,使用时根据实际情况进行设置。
o compact() -
从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
o mark() -
对position做出标记,配合reset使用
o reset() -
将position置为标记值
缓冲区的一些属性:
o capacity -
缓冲区大小,无论是读模式还是写模式,为激活时设定大小,此属性值不会变;
o position -
写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;
最大为capacity - 1;切换到读模式时,position会被置为0,表示当前读的位置从哪开始
o limit -
写模式下,limit
相当于capacity
表示最多可以向缓冲区写多少数据,切换到读模式时,
limit
等于原先的position,表示最多可以从缓冲区读多少数据。
下面是一个File的读写示例:case.txt中内容为this is test
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(100);
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
System.out.println("读取文件内容:"+new String(buffer.array(),0,bytes,"utf-8"));
}
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:100 容量是:100 位置为:0
字节数:15
写入缓冲区后限制数:100 容量是:100 位置为:15
读取文件内容:this is test
从以上这些结果中,看似正常,实际在应用中,我们的文件中的内容不可能只有100个字节以内,所以一旦超出100个字节,上面示例将会出现死循环,下面就针对这个问题进行修改:
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
StringBuilder builder = new StringBuilder();
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//使用clear获或者是flip
buffer.clear();//这里是将缓冲区从写模式转换为读模式,因为下面要将数据从缓冲区中将数据拿出来打印
// buffer.flip();//这里是对缓冲区进行翻转,如果这里两个方法一起用也将导致死循环
System.out.println("翻转缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
builder.append(new String(buffer.array(),0,bytes,"utf-8"));
}
System.out.println("读取文件内容:"+builder.toString());
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:10 容量是:10 位置为:0
字节数:10
写入缓冲区后限制数:10 容量是:10 位置为:10
翻转缓冲区后限制数:10 容量是:10 位置为:0
字节数:5
写入缓冲区后限制数:10 容量是:10 位置为:5
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容:this is test
以上修改后的代码,我将缓冲区容量改小了,文件中内容没变,从结果可以看出从通道写入缓冲区两次,写入缓冲区后pos位置是不一样的,是根据字节数的变化而变化,filp后或者是clear后,pos位置是都回到了初始化的位置。你可以认为是在filp或者clear后,你不指定起止位置的情况下,从缓冲区提取数据都是从0位置开始取到limit位置,包含空闲的缓冲区空间,而我这里是因为设置了起止值new
String(buffer.array(),0,bytes,"utf-8")。下面我们再来验证一下clear或者filp后,原缓冲区的数据是否还存在?
public static void readFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
System.out.println("写入缓冲区前限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
int bytes = -1;
//从通道中写入缓冲区,为写模式,buffer的position值变为从通道中读取的字节数
while((bytes = channel.read(buffer)) != -1){
System.out.println("字节数:"+bytes);
System.out.println("写入缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//使用clear获或者是flip
buffer.clear();//这里是将缓冲区从写模式转换为读模式,因为下面要将数据从缓冲区中将数据拿出来打印
// buffer.flip();//这里是对缓冲区进行翻转
System.out.println("翻转缓冲区后限制数:" + buffer.limit() + " 容量是:" + buffer.capacity()
+ " 位置为:" + buffer.position());
//将字节转换编码进行输出
System.out.println("读取文件内容:"+new String(buffer.array(),"utf-8"));
}
in.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}输出结果:
写入缓冲区前限制数:10 容量是:10 位置为:0
字节数:10
写入缓冲区后限制数:10 容量是:10 位置为:10
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容:this is
字节数:5
写入缓冲区后限制数:10 容量是:10 位置为:5
翻转缓冲区后限制数:10 容量是:10 位置为:0
读取文件内容: testis is //这里很明显的内容不对
通过以上结果可以看出,当你在缓冲区提取数据时,如果没有指定提取的起止位置,那么后面提取到的内容并不是我们想要的。也就是说我们在调用clear方法或者flip方法后,再次从通道中去写入数据到缓冲区,缓冲区中原有的数据是不会进行清除的,会直接根据本次所需空间进行覆盖,上面输出结果第二次读取5个字节写入到缓冲区,因为缓冲区最大容量10个字节.所以就会覆盖前5个字节,还保留着第一次缓冲区中的后5个字节内容,这样结合起来的数据就完全变了样。
一个从A文件到B文件的NIO读写例子:case.txt中内容不变
public static void writFileByNIO(){
File f = new File("C:\\Users\\zht\\Desktop\\case.txt");
File f2= new File("C:\\Users\\zht\\Desktop\\case2.txt");
try {
FileInputStream in = new FileInputStream(f);
//从文件字节流中获取一个文件通道
FileChannel channel = in.getChannel();
//构建一个缓冲区,并指定容量为100个字节
ByteBuffer buffer = ByteBuffer.allocate(10);
FileOutputStream out = new FileOutputStream(f2);
FileChannel channel2 =out.getChannel();
System.out.println("缓冲区输入前限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
int bytes = -1;
while((bytes = channel.read(buffer)) != -1){
System.out.println("缓冲区输入后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
ByteBuffer buffer2 = Charset.forName("utf-8").encode(new String(buffer.array(),0,bytes,"utf-8"));
while(buffer2.hasRemaining()){
channel2.write(buffer2);
System.out.println("缓冲区输出后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
}
buffer.flip();
System.out.println("缓冲区翻转后限制是:" + buffer.limit() + "容量是:" + buffer.capacity()
+ "位置是:" + buffer.position());
}
out.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
选择器:相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道。
通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:
o SelectionKey.OP_CONNECT
o SelectionKey.OP_ACCEPT
o SelectionKey.OP_READ
o SelectionKey.OP_WRITE
如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
通道向选择器注册时,会返回一个 SelectionKey对象,具有如下属性
o interest集合
o ready集合
o Channel
o Selector
o 附加的对象(可选)
用“位与”操作interest 集合和给定的SelectionKey常量,可以确定某个确定的事件是否在interest 集合中。
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
ready 集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set。Selection将在下一小节进行解释。
可以这样访问ready集合:
int readySet = selectionKey.readyOps();
可以使用以下四个方法获取已就绪事件,返回值为boolean:
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
可以将一个对象或者更多信息附着到SelectionKey上,即记录在附加对象上,方法如下:
selectionKey.attach(theObject);
Object attachedObj
= selectionKey.attachment();
可以通过选择器的select方法获取是否有就绪的通道,返回值表示上次执行select之后,就绪通道的个数。
int select();
int select(long timeout);
int selectNow();
可以通过selectedKeySet获取已就绪的通道。返回值是SelectionKey 的集合,处理完相应的通道之后,需要removed
因为Selector不会自己removed
select阻塞后,可以用wakeup唤醒;执行wakeup时,如果没有阻塞的select那么执行完wakeup后下一个执行select就会立即返回。
调用close() 方法关闭selector
相关文章推荐
- C语言对文件的读写操作以及处理CSV文件的方法
- MyEclipse设置JSP页面默认编码方式以及设置在Java文件中作者、日期等操作说明
- python之文件的读写和文件目录以及文件夹的操作实现代码
- 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射
- 整理javascript操作文件说明:读写文件
- java文件操作(普通文件以及配置文件的读写操作)
- c++ 简易文件读写操作
- 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射
- 关于文件的读写操作以及文件的打开和保存
- Android 文件读写以及assets操作
- net8:简易的文件磁盘管理操作一(包括文件以及文件夹的编辑创建删除移动拷贝重命名等)
- net8:简易的文件磁盘管理操作二(包括文件以及文件夹的编辑创建删除移动拷贝重命名等)
- 关于文件的读写操作以及文件的打开和保存
- C#对文件操作(基本的读写以及压缩和解压)
- 对串口的读写,将读取的内容写在文件里,以及一些文件的读写操作
- net9:磁盘目录文件保存到XML文档及其XML文档的读写操作,以及绑定XML到treeview
- PHP 文件读写操作(一)简易版
- java文件操作(普通文件以及配置文件的读写操作)
- 整理javascript操作文件说明:读写文件
- python中 对文件的读写操作 以及如何边写入 边保存flush()