Channel

前面讲到,NIO中一个连接就是用一个Channel(通道)来表示。大家知道,从更广泛的层面来说,一个通道可以表示一个底层的文件描述符,例如硬件设备、文件、网络连接等。然而,远远不止如此,除了可以对应到底层文件描述符,Java NIO的通道还可以更加细化。例如,对应不同的网络传输协议类型,在Java中都有不同的NIOChannel(通道)实现。

Channel(通道)的主要类型

这里不对纷繁复杂的Java NIO通道类型进行过多的描述,仅仅聚焦于介绍其中最为重要的四种Channel(通道)实现:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。
对于以上四种通道,说明如下:
(1)FileChannel文件通道,用于文件的数据读写。
(2)SocketChannel套接字通道,用于Socket套接字TCP连接的数据读写。
(3)ServerSocketChannel服务器嵌套字通道(或服务器监听通道),允许我们监听TCP连接请求,为每个监听到的请求,创建一个SocketChannel套接字通道。
(4)DatagramChannel数据报通道,用于UDP协议的数据读写。这个四种通道,涵盖了文件IO、TCP网络、UDP IO基础IO。下面从Channel(通道)的获取、读取、写入、关闭四个重要的操作,来对四种通道进行简单的介绍。

FileChannel通道

FileChannel是专门操作文件的通道。通过FileChannel,既可以从一个文件中读取数据,也可以将数据写入到文件中。特别申明一下,FileChannel为阻塞模式,不能设置为非阻塞模式。

//创建一条文件输入流 
FileInputStreamfis = new FileInputStream(srcFile); 
//获取文件流的通道 
FileChannelinChannel = fis.getChannel(); 
//创建一条文件输出流 
FileOutputStreamfos = new FileOutputStream(destFile); 
//获取文件流的通道 
FileChanneloutchannel = fos.getChannel(); 

//也可以通过RandomAccessFile文件随机访问类,获取FileChannel文件通道:
// 创建RandomAccessFile随机访问对象
RandomAccessFileaFile = new RandomAccessFile("filename.txt""rw"); 
//获取文件流的通道
FileChannelinChannel = aFile.getChannel();

读取FileChannel通道

在大部分应用场景,从通道读取数据都会调用通道的intread(ByteBufferbuf)方法,它从通道读取到数据写入到ByteBuffer缓冲区,并且返回读取到的数据量。

RandomAccessFile aFile = new RandomAccessFile(fileName, "rw"); 
//获取通道 
FileChannel inChannel=aFile.getChannel(); 
//获取一个字节缓冲区 
ByteBuffer buf = ByteBuffer.allocate(CAPACITY); 
int length = -1; 
//调用通道的read方法,读取数据并写入字节类型的缓冲区 
while ((length = inChannel.read(buf)) != -1) { 
//省略……处理读取到的buf中的数据 
} 

虽然对于通道来说是读取数据,但是对于ByteBuffer缓冲区来说是写入数据,ByteBuffer缓冲区处于写入模式。

写入FileChannel通道

写入数据到通道,在大部分应用场景,都会调用通道的intwrite(ByteBufferbuf)方法。此方法的参数——ByteBuffer缓冲区,是数据的来源。write方法的作用,是从ByteBuffer缓冲区中读取数据,然后写入到通道自身,而返回值是写入成功的字节数。此时的ByteBuffer缓冲区要求是可读的,处于读模式下。

//如果buf刚写完数据,需要flip翻转buf,使其变成读取模式 
buf.flip(); 
int outlength = 0; 
//调用write方法,将buf的数据写入通道 
while ((outlength = outchannel.write(buf)) != 0) { 
 System.out.println("写入的字节数:" + outlength); 
} 

关闭通道

当通道使用完成后,必须将其关闭。关闭非常简单,调用close方法即可。
//关闭通道

channel.close();

强制刷新到磁盘

在将缓冲区写入通道时,出于性能原因,操作系统不可能每次都实时将数据写入磁盘。如果需要保证写入通道的缓冲数据,最终都真正地写入磁盘,可以调用FileChannel的force()方法

//强制刷新到磁盘 
channel.force(true); 

特别强调一下,除了FileChannel的通道操作外,还需要注意ByteBuffer的模式切换。新建的ByteBuffer,默认是写入模式,可以作为inChannel.read(ByteBuffer)的参数。inChannel.read方法将从通道inChannel读到的数据写入到ByteBuffer。此后,需要调用缓冲区的flip方法,将ByteBuffer切换成读取模式,才作为outchannel. write(ByteBuffer)方法的参数,从ByteBuffer读取数据,再写入到outchannel输出通道。如此,便是完成一次复制。在进入下一次复制前,还要进行一次缓冲区的模式切换。ByteBuffer数据读完之后,需要将通过clear方法切换成写入模式,才能进入下一次的复制。

SocketChannel套接字通道

在NIO中,涉及网络连接的通道有两个,一个是SocketChannel负责连接传输,另一个是ServerSocketChannel负责连接的监听。NIO中的SocketChannel传输通道,与OIO中的Socket类对应。NIO中ServerSocketChannel监听通道,对应于OIO中的ServerSocket类。ServerSocketChannel应用于服务器端,而SocketChannel同时处于服务器端和客户端。换句话说,对应于一个连接,两端都有一个负责传输的SocketChannel传输通道。无论是ServerSocketChannel,还是SocketChannel,都支持阻塞和非阻塞两种模式。如何进行模式的设置呢?调用configureBlocking方法,具体如下:
(1)socketChannel.configureBlocking(false)设置为非阻塞模式。
(2)socketChannel.configureBlocking(true)设置为阻塞模式。
在阻塞模式下,SocketChannel通道的connect连接、read读、write写操作,都是同步的和阻塞式的,在效率上与Java旧的OIO的面向流的阻塞式读写操作相同。因此,在这里不介绍阻塞模式下的通道的具体操作。在非阻塞模式下,通道的操作是异步、高效率的,这也是相对于传统的OIO的优势所在。下面详细介绍在非阻塞模式下通道的打开、读写和关闭操作等操作。

DatagramChannel数据报通道

和Socket套接字的TCP传输协议不同,UDP协议不是面向连接的协议。使用UDP协议时,只要知道服务器的IP和端口,就可以直接向对方发送数据。在Java中使用UDP协议传输数据,比TCP协议更加简单。在Java NIO中,使用DatagramChannel数据报通道来处理UDP协议的数据传输。

获取DatagramChannel数据报通道

获取数据报通道的方式很简单,调用DatagramChannel类的open静态方法即可。然后调用configureBlocking(false)方法,设置成非阻塞模式。

读取DatagramChannel数据报通道数据

当DatagramChannel通道可读时,可以从DatagramChannel读取数据。和前面的SocketChannel的读取方式不同,不是调用read方法,而是调用receive(ByteBufferbuf)方法将数据从DatagramChannel读入,再写入到ByteBuffer缓冲区中。

写入DatagramChannel数据报通道

向DatagramChannel发送数据,和向SocketChannel通道发送数据的方法也是不同的。这里不是调用write方法,而是调用send方法。

关闭DatagramChannel数据报通道

这个比较简单,直接调用close()方法,即可关闭数据报通道。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值