Netty基础---NIO组件---Channel通道(三)

Channel(通道)

还不知道Channel(通道)在NIO处于什么位置的,可以去看看我Netty学习专题的第一篇

Netty基础—NIO简介(一)

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、SocketChannelFileChannel

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

转载请标注原作者,谢谢你们的支持,能给个小心心吗?
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉淀顶峰相见的PET

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值