天天看点

NIO与Socket编程之通道与FileChannel类的使用

2.1 通道概述

通道就是源缓冲区到目的缓冲区传输数据的通道。

Channel有11个子接口:

2.2.1 AsynchronousChannel接口的介绍

AsynchronousChannel接口的主要作用是使通道支持异步IO操作。异步IO操作有以下两种方式进行实现。

(1)方法

Future operation(…)

(2)回调

void operation(… A attachment,CompletionHandler<V,? super A> handler)

A类型的对象attachment的主要作用是让外部与CompletionHandler对象内部进行通信。使用CompletionHandler回调的方式实现异步IO操作的优点是CompletionHandler对象可以被复用。

当IO操作成功或失败时,CompletionHandler对象中的指定方法会被调用。

2.2.2 AsynchronousByteChannel接口的介绍

AsynchronousByteChannel接口的主要作用是使通道支持异步IO操作,操作单位是字节。

2.2.3 ReadableByteChannel接口的介绍

使通道允许对字节进行读操作。

ReadableByteChannel接口只允许有1个读操作在进行。如果1个线程正在1个通道上执行1个read()操作,那么任何试图发起另一个read()操作的线程都会被阻塞,直到第一个read()操作完成。

通道只接受以字节为单位的数据处理,因为通道和操作系统进行交互时,操作系统只接受字节数据。

2.2.4 ScatteringByteChannel接口的介绍

ScatteringByteChannel接口的主要作用是可以从通道中读取字节到多个缓冲区中。

2.2.5 WritableByteChannel接口的介绍

writableByteChannel接口只允许有1个写操作在进行。如果1个线程正在1个通道上执行1个write()操作,那么任何试图发起write操作的线程都会被阻塞。

WritableByteChannel接口有以下两个特点:

1.将1个字节缓冲区中的字节序列写入通道的当前位置;

2.write(ByteBuffer)方法是同步的。

2.2.6 GatheringByteChannel接口的介绍

GatheringByteChannel接口的主要作用是可以将多个缓冲区中的数据写入到通道中。

2.2.7 ByteChannel接口的介绍

没有任何方法,实现了ReadableByteChannel与WritableByteChannel接口,具有双向读写功能。

2.2.8 SeekableByteChannel接口的介绍

SeekalbeByteChannel接口的主要作用是在字节通道中维护position(位置)以及允许position发生改变。

2.2.9 NetworkChannel接口的介绍

NetworkChannel接口的主要作用是使通道与Socket进行关联,使通道中的数据能在Socket技术上进行传输。

2.2.10 MulticastChannel接口的介绍

MulticastChannel接口的主要作用是使通道支持Internet Protocal(IP)多播。IP多播就是将多个主机地址进行打包,形成一个组,让后IP报文向这个组进行发送,也就相当于同时向多个主机传输数据。

2.2.11 InterruptibleChannel接口的介绍

InterruptibleChannel接口的主要作用是使通道能以异步的方式进行关闭与中断。

2.3 AbstractInterruptibleChannel类的介绍

AbstractInterruptibleChannel类的主要作用是提供了一个可以被中断的通道基本实现类。

此类封装了能使通道实现异步关闭和中断所需要的最低级别的机制。在调用有可能无限期阻塞的IO操作的之前和之后,通道类必须分别调用begin和end()方法。

2.4 FileChannel类的使用

FileChannel类的主要作用是读取,写入,映射和操作文件的通道。该通道永远是阻塞的操作。

FileChannel类在内部维护当前文件的position,可对其进行查询和修改。该文件本身包含一个可读写、长度可变的字节序列,并且可以查询该文件的当前大小。当写入的字节超出当前文件大小时,则增加文件的大小,截取该文件时,则减少文件的大小。文件可能还有某个相关联的元数据,如访问权限,内容类型和最后的修改时间,但此类未定义访问元数据的方法。

除了字节通道中常见的读取、写入和关闭操作外,此类还定义了下列特定于文件的操作:

1.以不影响通道当前位置的方式,对文件中绝对位置的字节进行读取或写入。

2.将文件中某个区域直接映射到内存中。对于较大的文件,还通常比调用普通的read()或write()方法更为高效

3.强制对底层存储设备进行文件的更新,确保在系统崩溃时不丢失数据。

4.以一种可被很多操作系统优化为直接向文件系统缓存发送或从中读取的高速传输方法,将字节从文件传输到某个其他通道中,反之亦然。

5.可以锁定某个文件区域,以防止其他程序对其进行访问。

2.4.1 写操作与位置的使用

int write(ByteBuffer src)方法的作用是将remaining字节序列从给定的缓冲区写入此通道的当前位置。方法的返回值代表写入的字节数,可能为0。

WritableByteChannel接口有两个特点:

1.将1个ByteBuffer缓冲区中的remaining字节序列写入通道的当前位置。

2.write(ByteBuffer)方法是同步的

方法 long position()方法的作用是返回此通道的文件位置。

public abstract FileChannel position(long newPosition)方法的作用是设置此通道的文件位置

public class Test {
	public static void main(String[] args) throws IOException {
		try {
			FileOutputStream fos = new FileOutputStream("a.txt");
			FileChannel filechannel = fos.getChannel();
			ByteBuffer buffer = ByteBuffer.wrap("abcde".getBytes());
			System.out.println("当前通道位置:"+filechannel.position());
			System.out.println("写入通道的字节数:"+filechannel.write(buffer));
			System.out.println("写入后通道字节数:"+filechannel.position());
			//设定通道的位置
			filechannel.position(2);
			//缓冲区position回到0
			buffer.rewind();
			System.out.println("写入通道字节数:"+filechannel.write(buffer));
			System.out.println("通道现在的位置:"+filechannel.position());
		} catch (FileNotFoundException e) {
			
			e.printStackTrace();
		}
	
	}
}

运行结果:
当前通道位置:0
写入通道的字节数:5
写入后通道位置:5
写入通道字节数:5
通道现在的位置:7
           

2.4.2 写操作与位置的使用

int(ByteBuffer dst)方法的作用是将字节序列重此通道的当前位置读入给定的缓冲区的当前位置。此方法的行为与ReadableByteChannel接口中指定的行为完全相同。

2.4.3 批量写操作

long write(ByteBuffer [] srcs)方法的作用是将每个缓冲区的remaining字节序列写入此通道的当前位置。

long write(ByteBuffer[] srcs)方法实现的是GatheringByteChannel接口中的同名方法。

public class WriteBuffers {

	public static void main(String[] args) throws IOException {
       
		FileOutputStream fos = new FileOutputStream("a.txt");
		FileChannel fchannel = fos.getChannel();
		fchannel.write(ByteBuffer.wrap("123456".getBytes()));
		fchannel.position(3);
		ByteBuffer buffer1 = ByteBuffer.wrap("00002".getBytes());
		ByteBuffer buffer2 = ByteBuffer.wrap("00006".getBytes());
		ByteBuffer [] buffer = new ByteBuffer[] {buffer1,buffer2};
		fchannel.write(buffer);
		fchannel.close();
		fos.close();
	}

}

c.txt里的内容为:
1230000200006
           

2.4.4 批量读操作

long read(ByteBuffer[] dsts)方法的作用是将字节序列从此通道读入给定的缓冲区数组中的第0个缓冲区的当前位置。

2.4.5 设置位置与获得大小

position(long newPosition)方法的作用是设置此通道的文件位置。将该位置设置为大于文件当前大小的值是合法的,但不会更改文件的大小,稍后试图在这样的位置读取字节将立即返回已到达文件末尾的指示,稍后试图在这种位置写入字节将导致文件扩大,以容纳新的字节,在以前的文件末尾和新写入字节之间的字节值是未指定的。

2.4.6 截断缓冲区

truncate(long size)方法的作用是将此通道的文件截取为给定大小。如果给定大小小于该文件的当前大小,则截取该文件,丢弃新末尾后面的所有字节。如果给定大小大于或等于该文件的当前大小,则不修改文件。

2.4.7 将数据传输到其他可写入字节通道

long transferTo(position count,WritableByteChannel dest)方法的作用是将字节从此通道的文件传输到给定的可写入字节通道。transferTo()方法的功能相当于write()方法,只不过是将通道中的数据传输到另一个通道中,而不是缓冲区中。

long transferFrom(ReadableByteChannel src,position ,count)方法的作用是将字节从给定的可读取字节通道传输到此通道的文件中。

2.4.8 执行锁定操作

FileLock lock(long position,long size,boolean shared)方法的作用是获得此通道的文件给定区域上的锁定。在可以锁定该区域之前、已关闭此通道之前或者已中断调用线程之前,将阻塞此方法的调用。

文件锁定要么是独占的,要么是共享的。

2.4.9 FileLock lock()方法的使用

对整个文件的锁定

每次通过FileChannel类的lock()或tryLock()方法获取文件上的锁定时,就会创建一个FileLock(文件锁定)对象

文件锁定对象最初是有效的。

强烈建议在某个程序内使用唯一的通道来获取任意给定文件上的所有锁定。

2.4.10 boolean overlaps(long position,long size)方法的使用

boolean overlaps(long position,long size)方法的使用:判断此锁定是否与给定的锁定区域重叠。返回值是boolean类型,也就是当且仅当此锁定与给定的锁定区域至少重叠一个字节时,才返回true.

2.4.11 强制将所有对通道文件的更新写入包含文件的存储设备

void force(boolean metaData)方法的作用是强制将所有对此通道的文件更新写入包含该文件的存储设备中。

2.4.12 将通道文件区域直接映射到内存

MappedByteBuffer map(FileChannel.MapMode mode,long position,long size)方法的作用是将此通道的文件区域直接映射到内存中。

FileChannel.MapMode 有三种模式:

1.只读

2.读取/写入

3.专用

2.4.13 打开一个文件

FileChannel open(Path path,OpenOption… options)方法的作用是打开一个文件,以便对这个文件进行后期处理。

OpenOption 接口的接口实现类通常由StandardOpenOption枚举进行代替。

1.枚举量CREATE和WRITE的使用

单独使用CREATE并不会创建新文件,需结合WRITE(打开以进行写入访问)

2.枚举常量APPEND的使用

枚举常量APPEND的作用:如果打开文件以进行写入访问,则字节将写入文件末尾而不是开始处。

3.枚举常量READ的使用

枚举常量READ的作用:打开以进行读取访问

4.枚举常量TRUNCATE_EXISTING的使用

枚举常量TRUNCATE_EXESTING的使用:如果该文件已存在并且为写入访问而打开,则其长度将被截断为0。如果只为读取访问打开文件,则忽略此选项。

5.枚举常量CREATE_NEW的使用

枚举常量CREATE_NEW的作用:创建一个新文件,如果该文件已存在,则失败。

6.枚举常量DELETE_ON_CLOSE的使用

关闭时删除

7.枚举常量SPARSE的使用

枚举常量SPARSE的作用:稀疏文件。与CREATE_NEW选项一起使用时,此选项提供了一个提示,表明新文件将是稀疏的。如果当前文件系统不支持创建稀疏文件时,则忽略。

8.枚举常量SYNC的使用

要求对文件内容或元数据的每次更新都同步写入底层存储设备。如果这样做,程序运行的效率就降低了。

9.枚举常量DSYNC的使用

要求对文件内容的每次更新都同步写入底层存储设备。

与SYNC的区别:SYNC更新内容与元数据,而DSYNC只更新内容。

2.4.14 判断当前通道是否打开

public final boolean isOpen()方法的作用是判断当前的通道是否处于打开的状态。