Java-NIO篇章(3)——Channel通道类详解

03-01 1489阅读

Java NIO中,一个socket连接使用一个Channel(通道)来表示。对应到不同的网络传输协议类型,在Java中都有不同的NIO Channel(通道) 相对应。其中最为重要的四种Channel(通道)实现: FileChannel、 SocketChannel、 ServerSocketChannel、 DatagramChannel :

Java-NIO篇章(3)——Channel通道类详解
(图片来源网络,侵删)
  • FileChannel 文件通道,用于文件的数据读写; (管文件的传输通道)
  • SocketChannel 套接字通道,用于Socket套接字TCP连接的数据读写; (管TCP数据传输的通道)
  • ServerSocketChannel 服务器套接字通道(或服务器监听通道),允许我们监听TCP连接请求,为每个监听到的请求,创建一个SocketChannel套接字通道; (只管与服务器 TCP连接 的通道)
  • DatagramChannel 数据报通道,用于UDP协议的数据读写。 (管UDP数据传输的通道)

    我在学习Channel的时候,老是搞不清楚ServerSocketChannel和SocketChannel的关系,这次我不允许我的读者也搞不清。用大白话讲就是通道你可以理解为数据传输的管道,这个管道是双向传输的,即既可以通过Channel向文件或者网络客户端写数据也可以从文件或者网络客户端读数据。如果你要读取文件的数据,使用FileChannel ;如果需要建立网络连接,在服务器使用ServerSocketChannel 来作为客户端连接请求的通道,也就是说它只负责服务器端的连接请求的数据传输。通过ServerSocketChannel就可以和服务器建立连接,然后通过ServerSocketChannel创建SocketChannel 通道进行TCP数据传输。下面分别介绍每一个通道的用法。

    FileChannel文件通道

    FileChannel是专门操作文件的通道。通过FileChannel,既可以从一个文件中读取数据,也可以将数据写入到文件中。特别申明一下, FileChannel为阻塞模式,不能设置为非阻塞模式。不说你也知道,学习IO操作可以首先要获取FileChannel通道 、然后读取FileChannel通道中的数据或者将数据写入FileChannel通道,然后关闭通道。最后补充一个就是强制将通道的数据刷盘到磁盘的方法即可,那么就按照上面的步骤开始吧!

    获取到FileChannel对象

    获取FileChannel对象有三种方式,第一种方式可以通过文件的输入流、输出流获取FileChannel文件通道,代码如下:

    //创建一个文件输入流
    FileInputStream fis = new FileInputStream("word.txt");
    //获取文件流的通道,只能从通道中读取数据,不能写入数据
    FileChannel inChannel = fis.getChannel();
    //创建一个文件输出流
    FileOutputStream fos = new FileOutputStream("word.txt");
    //获取文件流的通道,只能向通道中写入数据,不能读取数据
    FileChannel outchannel = fos.getChannel();
    

    也可以通过RandomAccessFile文件随机访问类,获取FileChannel文件通道实例,代码如下:

    // 创建 既可以写也可以读的随机访问类 RandomAccessFile 随机访问对象
    // 参数"rw"表示可读可写,如果只读可以给"r",只写给"w"即可
    RandomAccessFile rFile = new RandomAccessFile("word.txt", "rw");
    //获取文件流的通道(可读可写)
    FileChannel channel = rFile.getChannel();
    

    从FileChannel中读取数据

    下面给出标准的读取数据的代码,具体解释在注释中,代码中channel.read(buffer)将通道的数据读到缓冲区上,虽然是读取通道的数据,对于通道来说是读取模式,但是对于ByteBuffer缓冲区来说则是写入数据,这时, ByteBuffer缓冲区处于写入模式 ,而buffer.get()才是从通道读取数据,需要flip()切换读模式:

    try(FileChannel channel = new RandomAccessFile("word.txt", "rw").getChannel()){
        // 准备缓冲区,分配10字节的空间
        ByteBuffer buffer = ByteBuffer.allocate(10);
        int len = -1;
        while ((len=channel.read(buffer))!=-1){ // 将channel中的数据读取到缓存区中,返回读到的数据长度,没读到数据返回-1
            buffer.flip(); // 切换读取模式,左右指针指向已存数据首位
            while (buffer.hasRemaining()){// 如果position
        log.error("文件未找到");
    }
    
    	// wrap 方法执行完自动切换wrapBuffer为读模式
    	ByteBuffer wrapBuffer = ByteBuffer.wrap("你好世界!".getBytes());
    	try(FileChannel channel = new RandomAccessFile("word.txt","rw").getChannel()){
            int len = 0;
            while ((len = channel.write(wrapBuffer))!=0){
            	System.out.println("已经写入字节数为:"+len); //已经写入字节数为:15
            }
        }catch (IOException e){
         	log.error("文件未找到");
    	}
    }
    // 关闭通道
    channel.close();
    //强制刷新到磁盘
    channel.force(true);
    
        try(
                FileChannel from = new FileInputStream("world.txt").getChannel();
                FileChannel to = new FileOutputStream("to.txt").getChannel()
        ) {
            long size = from.size();
            for (long left = size; left 0 ; ) {
                // 该方法每次最多传输2g的数据量
                long n = from.transferTo((size-left), from.size(), to);
                left -= n;
            }
        }catch (IOException ie){
            ie.printStackTrace();
        }
    }
    
    NIO提供的关于File的操作

    遍历目录文件:

    public class FileTest {
        public static void main(String[] args) throws IOException {
            // 访问文件夹
            visitorFile();
            // 拷贝文件夹
            copyFile();
            // 删除文件夹
            deleteFile();
        }
        private static void copyFile() throws IOException {
            String source = "C:\\Users\\cheney\\Documents\\CFSystem";
            String target = "C:\\Users\\cheney\\Documents\\CFSystem_bak";
            Files.walk(Paths.get(source)).forEach(path -> {
                // 替换成新的路径
                String targetName = path.toString().replace(source, target);
                try {
                    if(Files.isDirectory(path)){
                        Files.createDirectory(Paths.get(targetName));
                    }else if(Files.isRegularFile(path)){
                        Files.copy(path,Paths.get(targetName));
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }
            });
        }
        private static void deleteFile() throws IOException {
            Files.walkFileTree(Paths.get("C:\\Users\\cheney\\Documents\\CFSystem_bak"),new SimpleFileVisitor(){
                @Override
                // 在访问目录之前被调用。你可以在这里执行预处理操作。
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    // 进入文件夹时不能删除文件夹,因为里面还有文件
                    System.out.println("进入------>"+dir);
                    return super.preVisitDirectory(dir, attrs);
                }
                @Override
                // 在访问某个目录的文件时被调用。你可以在这里执行对文件的操作。
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    // 删除文件夹
                    System.out.println("删除xxxxxx:"+file);
                    Files.delete(file);
                    return super.visitFile(file, attrs);
                }
                @Override
                // 在访问文件失败时被调用。例如,由于权限问题或其他原因,无法访问文件。
                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                    return super.visitFileFailed(file, exc);
                }
                @Override
                // 在访问目录之后被调用。你可以在这里执行后处理操作。
                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                    // 如果退出之前遍历删除过文件,那么可以删除文件夹
                    System.out.println("退出
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]