NIO核心二:通道Channel

一、简单介绍

通道(Channel)是java.nio的第二个创建概念。Channel用于在缓冲区和位于通道另一侧的实体(通常是一个文件或者是一个套接字)之间有效的传输数据。只不过Channel本身不能直接访问数据,Channel只能和Buffer进行交互。
在这里插入图片描述
1.NIO的通道和流的区别

  • 通道可以同时进行读写,但是流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲读取数据,也可以写数据到缓冲区;

2.BIO中的stream是单向的,例如InputStream对象只能进行对数据的读取操作,而NIO中通道Channel是双向的,可以进行读操作,也可以进行写操作。

3.Channel在NIO中是一个接口

public interface Channel extends Closeable

二、常用的Channel实现类

  • FileChannel: 用于读取、写入、映射和操作文件通道。
  • DatagramChannel: 通过UDP读写网络中的数据通道。
  • SocketChannel: 通过TCP读取网络中的数据
  • ServerSocketChannel: 可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建SocketChannel。
    【ServerSocketChannel 类似 ServerSocket , SocketChannel 类似 Socket】

三、FileChannel类

1.FileChannel的常用方法

int read(ByteBuffer dst)Channel到中读取数据到ByteBuffer
long  read(ByteBuffer[] dsts)Channel到中的数据“分散”到ByteBuffer[]
int  write(ByteBuffer src)ByteBuffer 到中的数据写入到  Channel
long write(ByteBuffer[] srcs)ByteBuffer[] 到中的数据“聚集”到  Channel
long position() 返回此通道的文件位置
FileChannel position(long p) 设置此通道的文件位置
long size() 返回此通道的文件的当前大小
FileChannel truncate(long s) 将此通道的文件截取为给定大小
void force(boolean metaData) 强制将所有对此通道的文件更新写入到存储设备中

2.FileChannel的实际案例

①:需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道), 将 “hello world!” 写入到 data.txt 中.

public class NIOTest {public static void main(String[] args) {bufferTest();}public static void bufferTest(){try {// 1、字节输出流通向目标文件FileOutputStream fos = new FileOutputStream("D:\\data01.txt");// 2、得到字节输出流对应的通道ChannelFileChannel channel = fos.getChannel();// 3、分配缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("hello world!".getBytes());// 4、把缓冲区切换成写出模式buffer.flip();channel.write(buffer);channel.close();System.out.println("写数据到文件中!");} catch (Exception e) {e.printStackTrace();}}
}

②:需求:使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道),将data01.txt 中的数据读入到程序,并显示在控制台屏幕

public class NIOTest {public static void main(String[] args) throws Exception {bufferTest();}public static void bufferTest() throws Exception {// 1、定义一个文件字节输入流与源文件接通FileInputStream is = new FileInputStream("D:\\data01.txt");// 2、需要得到文件字节输入流的文件通道FileChannel channel = is.getChannel();// 3、定义一个缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 4、读取数据到缓冲区channel.read(buffer);buffer.flip();// 5、读取出缓冲区中的数据并输出即可String rs = new String(buffer.array(),0,buffer.remaining());System.out.println(rs);}
}

四、FileChannel类的创建方式

获取通道的一种方式是对支持通道的对象调用getChannel() 方法。
支持通道的类如下:

FileInputStream
FileOutputStream
RandomAccessFile
DatagramSocket
Socket
ServerSocket
获取通道的其他方式是使用 Files 类的静态方法 
newByteChannel() 获取字节通道。或者通过通道的静态
方法 open() 打开并返回指定通道

五、SocketChannel

SocketChannel是对传统的java socket API进行了改进,主要支持了非阻塞的读写,同时改进了传统的单向流API,Channel同时支持读和写(其实就是加了个中间层Buffer)

SocketChannel的基本使用

①:打开SocketChannel
创建SocketChannel的方式有两种分别是
方式一:

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com",80));

方式二:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("www.baidu.com",80));

②:关闭SocketChannel

socketChannel.close();

③:链接校验

socketChannel.isOpen(); //检测SocketChannel是否处于Open状态
socketChannel.isConnected(); // 检测SocketChannel是否已经被链接
socketChannel.isConnectionPending();//测试SocketChannel是否正在进行链接
socketChannel.finishConnect();//校验正在进行套接字的SocketChannel是否已经完成链接

④:SocketChannel的非阻塞模式
将 SocketChannel 设置为非阻塞模式之后,就可以在异步模式下调用connect(), read() 和write()了。

socketChannel.configureBlocking(false);

connect()
如果SocketChannel在非阻塞模式下,此时调用connect(),该方法可能在连接建立之前就返回了。为了确定连接是否建立,可以调用finishConnect()的方法。像这样:

public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open();//设置为非阻塞模式socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress("www.baidu.com",80));//校验正在进行套接字的SocketChannel是否已经完成链接while(! socketChannel.finishConnect() ){}
}

write()
非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了。所以需要在循环中调用write()。

public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("www.baidu.com", 80));String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48);buf.clear();buf.put(newData.getBytes());buf.flip();while(buf.hasRemaining()) {socketChannel.write(buf);}
}

read()
非阻塞模式下,read()方法在尚未读取到任何数据时可能就返回了。所以需要关注它的int返回值,它会告诉你读取了多少字节。

使用 SocketChannel 读写

从 SocketChannel 读取

public static void main(String[] argv) throws Exception {ByteBuffer buf = ByteBuffer.allocateDirect(1024);SocketChannel sChannel = SocketChannel.open();sChannel.configureBlocking(false);sChannel.connect(new InetSocketAddress("hostName", 12345));int numBytesRead = sChannel.read(buf);if (numBytesRead == -1) {sChannel.close();} else {buf.flip();}
}

写入 SocketChannel

public static void main(String[] argv) throws Exception {SocketChannel sChannel = SocketChannel.open();sChannel.configureBlocking(false);sChannel.connect(new InetSocketAddress("hostName", 12345));ByteBuffer buf = ByteBuffer.allocateDirect(1024);buf.put((byte) 0xFF);buf.flip();int numBytesWritten = sChannel.write(buf);
}

六、ServerSocketChannel

Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。

ServerSocketChannel的基本使用

①:打开 ServerSocketChannel

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

②:关闭 ServerSocketChannel
通过调用ServerSocketChannel.close() 方法来关闭ServerSocketChannel.

serverSocketChannel.close();

③:监听新进来的连接
通过 ServerSocketChannel.accept() 方法监听新进来的连接。当 accept()方法返回的时候,它返回一个包含新进来的连接的 SocketChannel。因此, accept()方法会一直阻塞到有新连接到达。

while(true){SocketChannel socketChannel = serverSocketChannel.accept();//do something with socketChannel...
}

当然,也可以在while循环中使用除了true以外的其它退出准则。
④:非阻塞模式
ServerSocketChannel可以设置成非阻塞模式。在非阻塞模式下,accept() 方法会立刻返回,如果还没有新进来的连接,返回的将是null。 因此,需要检查返回的SocketChannel是否是null.

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9999));serverSocketChannel.configureBlocking(false);while(true){SocketChannel socketChannel = serverSocketChannel.accept();if(socketChannel != null){//do something with socketChannel...}}

七、Scatter/Gather分散和聚集

分散(Scatter) 从Channel中读取是指在读操作时将读取的数据写入多个buffer中。因此,Channel将从Channel中读取的数据 “分散(Scatter)” 到多个Buffer中。
在这里插入图片描述

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);

注意buffer首先被插入到数组,然后再将数组作为channel.read() 的输入参数。read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写。
Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads才能正常工作。

聚集(gather) 写入Channel是指在写操作时将多个buffer的数据写入到同一个channel,因此,Channel 将多个Buffer中的数据“聚集(gather)”后发送到Channel。
在这里插入图片描述

public static void main(String[] argv) throws Exception {ByteBuffer header = ByteBuffer.allocate(128);ByteBuffer body   = ByteBuffer.allocate(1024);//write data into buffersByteBuffer[] bufferArray = { header, body };channel.write(bufferArray);
}

buffers数组是write()方法的入参,write()方法会按照buffer在数组中的顺序,将数据写入到channel,注意只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息。

scatter / gather经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头和消息体组成的消息,你可能会将消息体和消息头分散到不同的buffer中,这样你可以方便的处理消息头和消息体。

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

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

相关文章

Web自动化测试—webdriver的环境配置

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

猫毛过敏又不想扔掉猫怎么办?如何养猫?热门宠物空气净化器分享

养了猫咪一年多,忽然发现自己患上了过敏性鼻炎和结膜炎,就是那种一靠近猫咪就会不断打喷嚏、流鼻涕、流眼泪的症状。有时候还会感到眼睛发痒,发红。有没有什么好的方法治疗过敏性鼻炎呢? 医生建议,从根本上解决问题需…

数据结构(八)——初识单链表

😀前言 单链表是数据结构中最基本的一种链表结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。单链表具有灵活性和动态性,可以根据需要插入、删除和查找元素,适用于各种场景和问题的解决。 在本篇文章…

Java 属性可见性和TypeScripta 属性可见性区别

Java 中默认(无修饰符)的可见性对应的是包级私有(package-private),这是 Java 特有的可见性修饰符,有时也称为默认可见性,包级私有的可见性意味着只有同一个包中的其他类才能访问该成员&#xf…

STM32(7)NVIC

1. NVIC 1.1 NVIC简介 1.2 中断协作模型 1.3 NVIC的内部结构 1.4 中断向量表 1.4.2 在参考手册中查阅中断向量表

如何查看前端的vue项目是vue2还是vue3项目

1. 检查package.json文件 在项目的根目录下,打开package.json文件,查找dependencies或devDependencies部分中的vue条目。版本号将告诉你是Vue 2还是Vue 3。例如: Vue 2.x: "vue": "^2.x.x"Vue 3.x: "vue": &…

7.3 支付模块 - 创建订单、查询订单、通知

支付模块 - 创建订单、查询订单、通知 文章目录 支付模块 - 创建订单、查询订单、通知一、生成支付二维码1.1 数据模型1.1.1 订单表1.1.2 订单明细表1.1.3 支付交易记录表 1.2 执行流程1.3 Dto1.3.1 AddOrderDto 商品订单1.3.2 PayRecordDto支付交易记录扩展字段1.3.3 雪花算法…

(上海电力展)2024上海国际智慧电力与电气设备展览会

2024上海国际智慧电力与电气设备展览会 2024 Shanghai International Intelligent Power and Electrical Equipment Exhibition 时 间:2024年7月13-15日 地 点:上海新国际博览中心 展会简介Introduction 随着全球进入互联网和数字经济时…

【计算机考研】考408,还是不考408性价比高?

首先综合考虑,如果其他科目并不是很优秀,需要我们花一定的时间去复习,408的性价比就不高,各个科目的时间互相挤压,如果备考时间不充裕,考虑其他专业课也未尝不可。 复习408本来就是费力不讨好的事情 不同…

组合逻辑电路(一)(加法器)

目录 组合逻辑电路的特点及功能描述 采用SSI的组合逻辑电路的分析与设计 采用SSI的组合逻辑电路的分析 采用SII的组合逻辑电路的设计 例 例 例 常用的MSI组合逻辑电路 半加器 全加器 加法器 串行进位加法器 例 例 超前进位加法器 组合逻辑电路的特点及功能描述 小规模集成…

YOLOv9电动车头盔佩戴检测,详细讲解模型训练

向AI转型的程序员都关注了这个号👇👇👇 一、YOLOv9简介 YOLOv9是YOLO系列算法的最新版本。YOLO系列算法自2015年首次提出以来,已经在目标检测领域取得了显著的进展,以其快速和准确的特点而广受欢迎。 论文地址&#xf…

别再找了,关于免费SSL证书都在这里

免费SSL证书的优点: 成本效益:免费SSL证书可以帮助网站所有者节省资金,特别是对于初创公司或个人网站来说,这是一个很大的优势。提高信任度:通过使用SSL证书,网站可以向访问者展示其对安全性的承诺&#x…