Channel(通道)
还不知道Channel(通道)在NIO处于什么位置的,可以去看看我Netty学习专题的第一篇
Channel(通道)有一个特点,它是双向读写的,也就是说它不像流对象(FileInputStream/FileOutputStream)那样,只能读或写
我们来看看Channel的源码
public interface Channel extends Closeable {
//判断通道是否打开
public boolean isOpen();
//关闭通道
public void close() throws IOException;
}
从源码上看我们知道了Channel为一个接口,其主要方法还是在其实现类上,经常使用的Channel主要有三类:
ServerSocketChannel、SocketChannel:读写TCP的数据
FileChannel:读写文件类型的数据
DatagramChannel:读写UDP类型的数据
这里我们主要扩展前三类ServerSocketChannel、SocketChannel和FileChannel
ServerSocketChannel
Java NIO中的ServerSocketChannel是一个监听TCP连接的一个Channel,它是怎么监听的呢?给大家看个例子
首先我们先将通道打开
private ServerSocketChannel serverSocketChannel;
private static final int PORT = 7000;
try{
//调用ServerSocketChannel.open()打开通道
serverSocketChannel = ServerSocketChannel.open();
//绑定端口
serverSocketChannel.accept().bind(new InetSocketAddress(PORT));
//设置为非阻塞
serverSocketChannel.configureBlocking(false);
}catch (Exception e){
e.printStackTrace();
}finally{
//调用close()来关闭通道
serverSocketChannel.close();
}
然后定义一个监听方法
//监听方法
public void listen(){
try{
while(true){
//这里我们要搭配Selector来使用,在后面一章我会给大家展示一个完整的例子
//通过调用accept()实现对新连接的监听,而且是,连接请求不可能只有一条
SocketChannel sc = serverSocketChannel.accept();
sc.configureBlocking(false);
}
}catch (Exception e){
}
}
SocketChannel
它与ServerSocketChannel搭配使用,SocketChannel主要负责读写,把缓冲区的数据写入通道/把通道中的数据读到缓冲区,创建SocketChannel的方式有两种
1.当ServerSocketChannel接收到一个新的连接请求时,会创建一个SocketChannel来负责接收,也就是上面的代码
2.也可以自己创建一个SocketChannel来连接一台服务器
相关方法我直接使用代码告诉你们
package com.hecl.designmodels.NIO.GroupChat.Channels;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class demoSocketChannel {
public static void main(String[] args) {
try{
//打开一个SocketChannel
SocketChannel socketChannel = SocketChannel.open();
//连接到一个服务器
socketChannel.connect(new InetSocketAddress("http://localhost",7000));
//设置非阻塞模式
socketChannel.configureBlocking(false);
//读
//创建缓冲区
ByteBuffer byteBuffer_read = ByteBuffer.allocate(2048);
//标识读了多少数据,若为-1则读到了末尾
int count = socketChannel.read(byteBuffer_read);
//写
//创建缓冲区
ByteBuffer byteBuffer_write = ByteBuffer.allocate(2048);
//清空缓冲区
byteBuffer_write.clear();
//将数据写入缓冲区
byteBuffer_write.put("测试数据".getBytes());
//反转缓冲区
byteBuffer_write.flip();
//在这里我们不知道读取的buffer里还有没有数据,所以在这里做一个判断position < limit
while(byteBuffer_write.hasRemaining()){
socketChannel.write(byteBuffer_write);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
FileChannel
主要对本地文件进行I/O操作,我们先来看看FileChannel的主要方法
比较重要的我已经给大家框出来了
read():从通道读取数据并将其放入缓冲区
write(): 将缓冲区的数据写入通道中
transferTo():将数据从当前通道复制到目标通道
transferFrom():将数据从目标通道复制到当前通道
我们举个例子来看看它们的具体实现,这里注意一个点,FileChannel为抽象类不能实例化,这里我们通过FileInputStream、FileOutputStream来获取它的实现类
首先来看看**read()**的使用方法,先看看要读取的文件内容
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class demoFileChannel {
public static void main(String[] args) {
try{
//1.获取文件
File file = new File("E:\\ALiangX.txt");
//2.将文件转换为输入流
FileInputStream fileInputStream = new FileInputStream(file);
//3.得到FileChannel的实现类
FileChannel fileChannel = fileInputStream.getChannel();
//4.创建缓冲区,这里我们使用的是allocate来创建缓冲区,因为读取的只是本地的txt文本
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
//5.从通道读取数据并放入缓冲区
fileChannel.read(byteBuffer);
System.out.println(new String(byteBuffer.array()));
//6.别忘了调用close()
fileInputStream.close();
fileChannel.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
打印结果:
通过read()方法,我们就可以从通道读取数据了,这里给大家看看FileInputStream是怎么获取FileChannel的实现类的,主要是通过调用FileInputStream内部的getChannel方法来得到一个FileChannel实现类
**write()**使用方法,总体来说和read差别不大,直接上代码
package com.hecl.NIO.Channels;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class demoFileChannel_write {
public static void main(String[] args) {
try{
FileOutputStream fileOutputStream = new FileOutputStream("E:\\ALiangX_write.txt");
FileChannel fileChannel = fileOutputStream.getChannel();
//新建缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
//将数据放入缓冲区
byteBuffer.put("write()测试写入ALiangX".getBytes());
//切换为写模式
byteBuffer.flip();
//将缓冲区的数据写入通道
fileChannel.write(byteBuffer);
fileOutputStream.close();
fileChannel.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
只要有了read()和write()我们就可以完成一个简单文件的拷贝了,但是有一个问题,如果按照上边的方式进行拷贝,会多出一次拷贝,用一张图来表达你们就明白了
也就是说,多了一次缓冲区的拷贝,我们能不能直接从这个通道直接拷贝到另一个通道呢?FileChannel里就为我们提供了transferFrom、transferTo两个方法,用哪个都可以
我们通过代码来看看具体是怎么使用的
package com.hecl.NIO.Channels;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
public class demoFileChannel_transfer {
public static void main(String[] args) {
try{
//构造流对象
FileInputStream fileInputStream = new FileInputStream("E:\\test_transferFrom.png");
FileOutputStream fileOutputStream = new FileOutputStream("E:\\test_To.png");
//获取对应的通道
FileChannel from = fileInputStream.getChannel();
FileChannel to = fileOutputStream.getChannel();
//调用transferFrom方法完成拷贝
to.transferFrom(from,0,from.size());
// from.transferTo(0,from.size(),to);
//调用close()
fileInputStream.close();
fileOutputStream.close();
from.close();
to.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
测试结果
通过这两个方法,我们既减少了一次拷贝并且完成了目标对象的拷贝工作,何乐而不为呢
下期我们主要讲Selector(选择器)
完成:2021/3/27 16:57 ALiangX
转载请标注原作者,谢谢你们的支持,能给个小心心吗?