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)

2.6 使用FileChannle 完成文件发复制实战

需求:使用FileChannel复制文件,把原文件中的内容复制到目标文件中。

public class FileNIOCopyDemo {//原文件路径public static final String srcPath = "";//目标文件路径public static final String destPath = "";public static void main(String[] args) {//演示复制资源文件nioCopyFile(srcPath,destPath);}public static void nioCopyFile(String srcPath, String destPath){File src_file = new File(srcPath);File dest_file = new File(destPath);try {//如果目标文件不存在,则新建if(!dest_file.exists()){dest_file.createNewFile();}long start_time = System.currentTimeMillis();FileInputStream fis = null;FileOutputStream fos = null;//输入通道FileChannel inChannel = null;//输出通道FileChannel outChannel = null;try {fis = new FileInputStream(src_file);fos = new FileOutputStream(dest_file);inChannel = fis.getChannel();outChannel = fos.getChannel();int length = -1;//新建buf,处于写模式ByteBuffer buffer = ByteBuffer.allocate(1024);//从输入通道读取bufferwhile((length = inChannel.read(buffer)) != -1){//buffer第一次切换成功,从写模式变成读模式buffer.flip();int outlength = 0;//把buffer写入输出的通道while ((outlength = outChannel.write(buffer)) != 0){System.out.println("写入的字节数:" + outlength);}//buffer第二次模式切花,清除buffer,变成写模式buffer.flip();}//强制刷新到磁盘outChannel.force(true);}finally {outChannel.close();fos.close();inChannel.close();fis.close();}long end_time = System.currentTimeMillis();System.out.println("复制花费的时间是:" + (end_time - start_time) + "毫秒!");}catch (Exception e){}}
}

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();

3.6 使用 SocketChannel 发送文件 实战

        需求:使用FileChannel读取本地文件内容,然后在客户端使用SocketChannel 把文件信息和文件内容发送到服务器。

public class NioSendClient {private Charset charset = Charset.forName("UTF-8");public static final String SMALL_PATH = "/Users/jay/Documents/files/src_nio.txt";public static final String BIG_PATH = "/Users/jay/Documents/files/孔乙己.docx";//向服务器传输文件public void sendFile() throws Exception {File file = new File(BIG_PATH);FileChannel fileChannel = new FileInputStream(file).getChannel();SocketChannel socketChannel = SocketChannel.open();socketChannel.socket().connect(new InetSocketAddress("127.0.0.1",18899));socketChannel.configureBlocking(false);System.out.println("成功连接服务端");while(!socketChannel.finishConnect()){int count = 0;//不断自旋、等待,或者做一些其他的是System.out.println(count ++);}//发送文件的名称ByteBuffer fileNameByteBuffer = charset.encode(file.getName());ByteBuffer buffer = ByteBuffer.allocate(1024);//发送的文件长度int fileNameLen = fileNameByteBuffer.remaining();buffer.clear();buffer.putInt(fileNameLen);//切换到读模式buffer.flip();socketChannel.write(buffer);System.out.println("Client 文件名称长度发送完成:"+fileNameByteBuffer);//发送文件socketChannel.write(fileNameByteBuffer);System.out.println("Client 文件名称名称发送完成:" + file.getName());//清空buffer.clear();buffer.putInt((int) file.length());//切换到读模式buffer.flip();//写入文件长度socketChannel.write(buffer);System.out.println("Client 文件长度发送完成:"+ file.length());//发送文件内容System.out.println("开始传输文件");int length = 0;long offset = 0L;buffer.clear();while((length = fileChannel.read(buffer)) > 0){buffer.flip();socketChannel.write(buffer);offset += length;System.out.println("| " + (100 * offset / file.length()) + "% |");buffer.clear();}//等待一分钟关闭连接Thread.sleep(60000);if (length == -1){fileChannel.close();socketChannel.shutdownOutput();socketChannel.close();}System.out.println("文件传输成功");}}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【GitHub项目推荐--GitHub 上的考研神器】【转载】

如果有打算考研的读者,这些开源项目不能错过。把各个学校近几年考研初试真题分享给大家(包括 408),应该能帮上大家,文末有下载方式。 同时,我把盘点的开源相关的学习项目更新到 Awesome GiHub Repo&#xf…

【GitHub项目推荐--智能家居项目】【转载】

如果你具备硬件、软件知识,这个项目肯定符合你的胃口。 物美智能是一套软硬件结合的开源项目,该系统可助你快速搭建自己的智能家居系统。你可以学习到设备的集成和软硬件交互。 PC 端或者手机与服务端通信,单片机可以接受遥控设备和服务器的…

硬件-11-服务器的基础知识

参考服务器基础知识大科普 1 电视剧背景 服务器被誉为互联网之魂。 电视剧《创业年代》是一部有冯绍峰和袁姗姗等人联手主演的一部讲述我国第一批科技创业者创业故事的电视剧,可以说是他们铲下了建设中关村的第一捧土。 电视剧《创业年代》中的潮信公司并没有…

【神经网络】火箭点火发射-诠释一场数据与学习的奇妙之旅

火箭点火发射来理解神经网络的故事细节 在一个充满科技气息的研究室里,一群科学家们正在忙碌地准备着一次重要的火箭点火发射。这次发射不仅是一次航天探索的壮丽征程,更是一场利用神经网络处理数据的智慧之旅。 在火箭发射的背后,神经网络…

中仕教育:研究生毕业可以考选调生吗?

选调生的报考条件之一是应届生,研究生毕业也属于应届生,所以是可以报考的。 选调生不同学历的年龄限制: 1.应届本科生:年龄在25岁以内 2.应届研究生:年龄在30岁以内 3.应届博士生:年龄在35岁以内 研究…

excel统计分析——Tukey法多重比较

参考资料:生物统计学 https://real-statistics.com/one-way-analysis-of-variance-anova/unplanned-comparisons/tukey-hsd/ Tukey法是基于学生化极差分布计算最小显著极差(LSR),根据平均数个数调整最小显著极差。 LSR&#xff1…

LINUX常用工具之sudo权限控制

一、Sudo基本介绍 sudo是Linux 中用于允许特定用户以超级用户或其他特权用户的身份执行特定的命令或任务。sudo 提供了一种安全的方法,使用户能够临时获取额外的权限,而不需要以完全超级用户的身份登录系统。sudo也可以用了设置黑名单命令清单&#xff…

ROS第 13 课 TF 坐标系广播与监听的编程 实现

文章目录 第 13 课 TF 坐标系广播与监听的编程 实现1.机器人的坐标变换2.创建功能包3.编程方法3.1 编写广播和监听程序3.2 运行程序 第 13 课 TF 坐标系广播与监听的编程 实现 1.机器人的坐标变换 在进行编程前,先需要了解机器人的坐标变换。这里以运行海龟案例来…

有关软件测试的,任何时间都可以,软件测试主要服务项目:测试用例 报告 计划

有关软件测试的,任何时间都可以,软件测试主要服务项目: 1. 测试用例 2. 测试报告 3. 测试计划 4. 白盒测试 5. 黑盒测试 6. 接口测试 7.自动…

Vuex的基础使用

在使用之前要先了解Vuex的组成结构,跟对应的使用关系。 在上图的结构图中可以看到四个组成部分,首先是Components(组件)、Actions(行动)、Mutations(变化)、state(状态/数…

Vue——计算属性

文章目录 计算属性computed 计算属性 vs methods 方法计算属性完整写法 综合案例:成绩案例 计算属性 概念:基于现有的数据,计算出来的新属性。依赖的数据变化,自动重新计算 语法: ①声明computed配置项中,一个计算属性…

gin渲染篇

1. 各种数据格式的响应 json、结构体、XML、YAML类似于java的properties、ProtoBuf package mainimport ("github.com/gin-gonic/gin""github.com/gin-gonic/gin/testdata/protoexample" )// 多种响应方式 func main() {// 1.创建路由// 默认使用了2个中…

链表中倒数第k个结点(附带源码)

目录 代码部分: 核心:看图 代码部分: struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {// write code here// write code hereif (k 0){return NULL;}else{struct ListNode* slow pListHead, * fast pListHead;//…

Linux内核pinctrl子系统驱动框架

一. 简介 本文简单了解一下Linux内核代码中, pinctrl子系统的驱动实现。 注意:本文会涉及到 Linux 驱动分层与分离、平台设备驱动等还未讲解的知识 ,所以,也不会影响后续的实验。 二. Linux内核pinctrl子系统驱动 1. probe函…

【数学建模】数据处理与可视化

文章目录 数值计算工具NumPy数组的创建、属性和操作数组的运算、通用函数和广播运算Numpy.random模块的随机数生成文本文件和二进制文件存取 文件操作文件基本操作文件管理方法 数据处理工具PandasSeries和DataFrame外部文件存取 Matplotlib可视化基础用法可视化应用可视化综合…

2023年DevOps国际峰会暨 BizDevOps 企业峰会(DOIS北京站):核心内容与学习收获(附大会核心PPT下载)

随着科技的飞速发展,软件开发的模式和流程也在不断地演变。在众多软件开发方法中,DevOps已成为当下热门的软件开发运维一体化模式。特别是在中国,随着越来越多的企业开始认识到DevOps的价值,这一领域的研究与实践活动日益活跃。本…

反序列化字符串逃逸(下篇)

这里承接上篇文章反序列化字符串逃逸&#xff08;上篇&#xff09;-CSDN博客带大家学习反序列化字符串逃逸减少&#xff0c;没有看过的可以先去看看&#xff0c;不会吃亏。 例题&#xff1a; <?php highlight_file(__FILE__); error_reporting(0); function filter($name…

Linux_清理docker磁盘占用

文章目录 前言一、docker system 命令1. docker system df&#xff08;本文重点使用&#xff09;2. docker system prune&#xff08;本文重点使用&#xff09;3. docker system info4. docker system events 二、开始清理三、单独清理Build Cache四、单独清理未被使用的网络 前…

nginx虚拟主机

虚拟主机指的就是一个独立的站点配置&#xff0c;是nginx默认支持的一个功能&#xff0c;它能够有自己独立的域名&#xff0c;独立的ip&#xff0c;独立的端口配置&#xff0c;能够配置完整的www服务&#xff0c;列如网站搭建&#xff0c;邮件服务器代理等等。并且nginx支持多虚…

回归预测 | Python基于Encoder-TCN-BIGRU-Decoder多变量回归预测

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.支持多输入&#xff0c;单输出&#xff01; 2.使用了多种可视化方法&#xff0c;代码编写过程中也对各段代码进行封装&#xff0c;方便解读和调试&#xff01; 3.适合于风电数据&#xff0c;光伏数据&#xff0c;环…