Java NIO (三)NIO Channel类

1 概述

        前面提到,Java NIO中一个socket连接使用一个Channel来表示。从更广泛的层面来说,一个通道可以表示一个底层的文件描述符,例如硬件设备、文件、网络连接等。然而,远不止如此,Java NIO的通道可以更加细化。例如,不同的网络传输协议,在Java中都有不同的NIO Channel实现。

        这里不对Java NIO的全部通道类型进行过多描述,仅着重介绍其中最为重要的四种Channle实现:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。

        对于以上四种通道,说明如下:

        (1)FileChannel:文件通道,用于文件的数据读写。

        (2)SocketChannel:套接字通道,用于套接字TCP连接的数据读写。

        (3)ServerSocketChannel:服务器套接字通道(或服务器监听通道),允许监听TCP连接请求,位每个监听的请求创建一个SocketChannel通道。

        (4)DatagramChannel:数据报通道,用于UDP的数据读写。

        这四种通道涵盖了文件IO、TCP网络、UDP IO三类基础IO读写操作。

2 FileChannel

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

        下面分别介绍FileChannel的获取、读取、写入、关闭四个操作。

        2.1 获取FileChannel

        

public class Test_FileChannel {
    final static String srcFile = "/Users/jay/Documents/files/nio.txt";
    final static String outFile = "/Users/jay/Documents/files/nio1.txt";
    public static void main(String[] args) throws IOException {
        //创建一个文件输入流
        FileInputStream fis = new FileInputStream(srcFile);
        //获取文件流的通道
        FileChannel inChannel = fis.getChannel();
        //创建一个文件输出流
        FileOutputStream fos = new FileOutputStream(outFile);
        //获取文件流的通道
        FileChannel outChannel = fos.getChannel();
        System.out.println(outChannel.size());

    }
}

2.2 读取FileChannle

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

public class Test_FileChannel {
    final static String srcFile = "/Users/jay/Documents/files/nio.txt";
    final static String outFile = "/Users/jay/Documents/files/nio1.txt";
    public static void main(String[] args) throws IOException {
        RandomAccessFile rw = new RandomAccessFile(srcFile, "rw");
        //获取通道(可读可写)
        FileChannel channel = rw.getChannel();
        //获取一个字节缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(30);
        int length = -1;
        //调用通道read方法,读取数据并写入字节类型的缓冲区
        while((length = channel.read(buffer)) != -1){
            System.out.println(length);
        }

    }
}

2.3 写入FileChannel

        把数据写入通道,在大部分引用场景中都会调用通道的write(ByteBuffer buf)方法,此方法的参数是一个ByteBuffer缓冲区示例,是待写数据的来源。

        write(ByteBuffer buf)方法的作用是从ByteBuffer缓冲区中读取数据,然后写入通道自身,而返回值是写入成功的字节数。 

public class Test_FileChannel {
    final static String srcFile = "/Users/jay/Documents/files/nio.txt";
    final static String outFile = "/Users/jay/Documents/files/nio1.txt";
    public static void main(String[] args) throws IOException {
        RandomAccessFile rw = new RandomAccessFile(srcFile, "rw");
        //获取通道(可读可写)
        FileChannel channel = rw.getChannel();
        //获取一个字节缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(30);
        int length = -1;
        //调用通道read方法,读取数据并写入字节类型的缓冲区
        while((length = channel.read(buffer)) != -1){
            System.out.println(length);
        }
        buffer.flip();
        int outlength = 0;
        while((outlength = channel.write(buffer)) != 0){
            System.out.println("写入的字节数 = " + outlength);
        }
    }
}

         

2.4  关闭通道

        当通道使用完后,必须将其关闭。调用close()方法 .

public class Test_FileChannel {
    final static String srcFile = "/Users/jay/Documents/files/nio.txt";
    final static String outFile = "/Users/jay/Documents/files/nio1.txt";
    public static void main(String[] args) throws IOException {
        RandomAccessFile rw = null;
        FileChannel channel = null;
        try {
             rw = new RandomAccessFile(srcFile, "rw");
            //获取通道(可读可写)
            channel = rw.getChannel();
            //获取一个字节缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(30);
            int length = -1;
            //调用通道read方法,读取数据并写入字节类型的缓冲区
            while((length = channel.read(buffer)) != -1){
                System.out.println(length);
            }
            buffer.flip();
            int outlength = 0;
            while((outlength = channel.write(buffer)) != 0){
                System.out.println("写入的字节数 = " + outlength);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            channel.close();
        }

    }
}

2.5 强制刷新到磁盘

        在将缓冲区写入通道时,出于性能的原因,操作系统不可能每次都实时地将写入数据保存到硬盘,完成最终的数据保存。在将缓冲区数据写入通道时,要保证数据能写入硬盘,可以在写入后调用FileChannel类的force()方法。channel.force(true)

3 SocketChannel

        3.1 概述

        在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的连接、读写操作都是同步阻塞式的,在效率上与Java OIO面向流的阻塞式读写操作相同。因此,在这里不介绍阻塞模式下通道的具体操作。在非阻塞模式下,通道的操作是异步、高效的,这也是相对于传统的OIO的优势所在。

        3.2 获取SocketChannel传输通道

【重要通知:本章小节里的代码只是方法演示,不具备运行功能。在介绍完SocketChannel常用的方法后,会举例并成功运行】

        在客户端,先通过SocketChannel静态方法open()获得一个套接字传输通道,然后将socket设置为非阻塞模式,最后通过connet()实例方法对服务器的IP和端口发起连接。在非阻塞情况下,与服务器的连接可能还没有建立,socketChannel.connect()方法就反悔了,因此需要不断地自旋,检查当前是否连接到主机。

客户端:ClientTest.java

public class ClientTest {
    public static void main(String[] args) {
        //获取一个套接字传输通道
        try {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("127.0.0.1",80));
            while(! socketChannel.finishConnect()){
                System.out.println("连接成功");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

        在连接建立的事件到来时,服务端的ServerSocketChannel能成功地查询出这个新连接事件,并且通过调用服务端ServerSocketChannel监听套接字的accept()方法来获取新连接的套接字通道。

服务端:ServerTest.java

public class ServerTest {
    public static void main(String[] args) throws IOException {
        //新连接事件到来,首先通过事件获取服务器监听通道
        //key是具体的什么事件,是否为新连接事件-后面会说在,这里理解为一个事件对象
        ServerSocketChannel server = (ServerSocketChannel) key.channel();
        //获取新连接的套接字通道
        SocketChannel socketChannel = server.accept();
        //设置为非阻塞模式
        socketChannel.configureBlocking(false);
    }
}

【说明】NIO套接字通道主要用于非阻塞的传输场景。

3.3 读取SocketChannel传输通道

        当SocketChannel传输通道可读时,可以从SocketChannel读取数据,具体方法与前面的文件通道读取方法时相同的。调用read()方法,将数据读入缓冲区ByteBuffer。

ByteBuffer buffer = ByteBuffer.allocate(1024);
int byteRead = socketChannel.read(buffer);

        在读取时,因为是异步的,所以我们必须检查read()的返回值,以便判断当前是否读取了数据。read()方法的返回值是读取的字节数,如果是-1,那么表示读取到对方的输出结束标志,即对方已经输出结束,准备关键连接。实际上,通过read()方法读数据本身是很简单的,比较困难的是在非阻塞模式下如何知道通道何时是可读的。这需要用到NIO的新组件——Selector通道选择器,稍后会介绍它。

3.4 写入SocketChannel传输通道

        和前面把数据写入FileChannel一样,大部分应用场景都会调用通道的write(ByteBuffer buf)方法。

//写入缓冲区前要求ByteBuffer是读模式
buffer.flip();
socketChannel.write(buffer);

3.5 关闭SocketChannel传输通道

        在关闭SocketChannel传输通道前,如果传输通道用来写入数据,则建议调用一次shutdownOutput()终止输出方法,向对方发送一个输出的结束标志(-1),然后调用socketChannel.close()方法,关闭套接字连接。

SocketChannel socketChannel = SocketChannel.open();
socketChannel.shutdownOutput();
socketChannel.close();

4 DategramChannel 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/330540.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实战 | OpenCV两种不同方法实现粘连大米粒分割计数(步骤 + 源码)

导 读 本文主要介绍基于OpenCV的两种不同方法实现粘连大米分割计数,并给详细步骤和源码。源码和图片素材见文末。 背景介绍 测试图如下,图中有个别米粒相互粘连,本文主要演示如何使用OpenCV用两种不同方法将其分割并计数。 方法一:基于分水岭算法 基于分水岭算法…

【playwright】新一代自动化测试神器playwright+python系列课程18_playwritht元素相关操作_等待元素到某种状态

元素相关操作_等待元素到某种状态 对于自动化测试来说,本质上就是定位元素、操作元素。网页上的元素有不同状态,有些元素本来不在网页的DOM中,经过某一步操作后才出现。有些元素是本来就已经在DOM中但是是隐藏的状态,经过某一步操…

K8S--解决访问Harbor私有仓库无权限的问题(401 Unauthorized)

原文网址:K8S--解决访问Harbor私有仓库无权限的问题(401 Unauthorized)-CSDN博客 简介 本文解决K8S访问Harbor私有仓库无权限的问题:401 Unauthorized。 问题复现 用Harbor部署了私有仓库,将镜像推送上去。指定私有…

python数字图像处理基础(五)——Canny边缘检测、图像金字塔、图像分割

目录 Canny边缘检测原理步骤 图像金字塔1.高斯金字塔2.拉普拉斯金字塔 图像分割图像轮廓检测1.检测轮廓2.绘制轮廓3.补充 Canny边缘检测 梯度是什么? 梯度就是变化的最快的那个方向 edge cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]…

第90讲:MySQL数据库主从复制集群原理概念以及搭建流程

文章目录 1.MySQL主从复制集群的核心概念1.1.什么是主从复制集群1.2.主从复制集群中的专业术语1.3.主从复制集群工作原理1.4.主从复制中的小细节1.5.搭建主从复制集群的前提条件1.6.MySQL主从复制集群的架构信息 2.搭建MySQL多实例环境2.1.在mysql-1中搭建身为主库的MySQL实例2…

小程序 自定义组件和生命周期

文章目录 ⾃定义组件创建⾃定义组件声明组件编辑组件注册组件 声明引⼊⾃定义组件⻚⾯中使⽤⾃定义组件定义段与⽰例⽅法组件-⾃定义组件传参过程 小程序生命周期应用生命周期页面生命周期页面生命周期 ⾃定义组件 类似vue或者react中的自定义组件 ⼩程序允许我们使⽤⾃定义组件…

设计模式的学习笔记

设计模式的学习笔记 一. 设计模式相关内容介绍 1 设计模式概述 1.1 软件设计模式的产生背景 设计模式最初并不是出现在软件设计中,而是被用于建筑领域的设计中。 1977 年美国著名建筑大师、加利福尼亚大学伯克利分校环境结构中心主任 Christopher Alexander 在…

【动态规划】【数学】【C++算法】18赛车

作者推荐 视频算法专题 本文涉及知识点 动态规划 数学 LeetCode818赛车 你的赛车可以从位置 0 开始,并且速度为 1 ,在一条无限长的数轴上行驶。赛车也可以向负方向行驶。赛车可以按照由加速指令 ‘A’ 和倒车指令 ‘R’ 组成的指令序列自动行驶。 当…

情人节专属--html5 canvas制作情人节告白爱心动画特效

💖效果展示 💖html展示 <!doctype html> <html> <head> <meta charset=

2023年移远车载全面开花,智能座舱加速进击

作为汽车智能化的关键组件&#xff0c;车载模组正发挥着越来越重要的作用。 移远通信进入车载模组领域近十年&#xff0c;已形成了完善的车载产品队列&#xff0c;不但在5G/4G车载通信、智能座舱、C-V2X车路协同等领域打造了一枝独秀的产品线&#xff0c;也推出了车规级Wi-Fi/蓝…

解决springboot启动报Failed to start bean ‘subProtocolWebSocketHandler‘;

解决springboot启动报 Failed to start bean subProtocolWebSocketHandler; nested exception is java.lang.IllegalArgumentException: No handlers 问题发现问题解决 问题发现 使用springboot整合websocket&#xff0c;启动时报错&#xff0c;示例代码&#xff1a; EnableW…

大数据时代的黄金机遇:阿里云大数据分析师ACP认证【一条龙服务100%通过】

扫码和我联系 随着大数据技术的迅速发展和广泛应用&#xff0c;成为了当今时代最具吸引力的技术之一。为了让更多技术人才把握这一时代机遇&#xff0c;阿里云推出了大数据分析师ACP认证&#xff08;Alibaba Cloud Certified Professional - Data Analyst&#xff09;&#xf…

数据结构:顺序栈

栈是一种先进后出的数据结构&#xff0c;只允许在一端&#xff08;栈顶&#xff09;操作&#xff0c;代码中top表示栈顶。 stack.h /* * 文件名称&#xff1a;stack.h * 创 建 者&#xff1a;cxy * 创建日期&#xff1a;2024年01月17日 * 描 述&#xff1a; …

LeetCode、2542. 最大子序列的分数【中等,排序+小顶堆】

文章目录 前言LeetCode、2542. 最大子序列的分数【中等&#xff0c;排序小顶堆】题目及类型思路及代码实现 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领…

基于Springboot的摄影分享网站系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的摄影分享网站系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

GBASE南大通用数据库GBase BI V5支持的集群部署

GBaseBI V5可以单独部署在一个服务器上&#xff0c;在单套的情况下安装成功后不需要特殊的设置即可直接使用。某些用户的应用并发数可能很多&#xff0c;单个服务器处理请求太慢&#xff0c;GBaseBI V5支持集群和分布式部署。其中集群部署如下图所示&#xff1a; 集群部署 在集…

【Vue3】2-13 : 章节总结

本书目录&#xff1a;点击进入 一、总结内容 二、习题 2.1 【选择题】以下Vue指令中&#xff0c;哪些指令具备简写方式&#xff1f; 2.2 【编程题】以下Vue指令中&#xff0c;哪些指令具备简写方式&#xff1f; &#xff1e; 效果 &#xff1e; 代码 一、总结内容 了解核…

《WebKit 技术内幕》之三(3): WebKit 架构和模块

3 Webkit2 3.1 Webkit2 架构及模块 相比于狭义的WebKit&#xff0c;WebKit2是一套全新的结构和接口&#xff0c;而并不是一个简单的升级版。Webkit2 的思想同 Chrominum 类似&#xff0c;就是将渲染过程放在单独的进程中来完成&#xff0c;独立于用户界面。 webKit2中…

ARM 1.12

norflash与nandflash的区别&#xff1a; 一、NAND flash和NOR flash的性能比较 1、NOR的读速度比NAND稍快一些。 2、NAND的写入速度比NOR快很多。 3、NAND的4ms擦除速度远比NOR的5s快。 4、大多数写入操作需要先进行擦除操作。 5、NAND的擦除单元更小&#xff0c;相应的擦除电…

嵌入式软件工程师面试题——2025校招社招通用(二十一)

说明&#xff1a; 面试群&#xff0c;群号&#xff1a; 228447240面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但…