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、得到字节输出流对应的通道Channel
            FileChannel 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 buffers
    ByteBuffer[] 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.mfbz.cn/a/440539.html

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

相关文章

基于springboot+vue的周边游平台个人管理系统

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…

算法刷题day25:多路归并

目录 引言概念一、鱼塘钓鱼二、技能升级三、序列 引言 关于这个多路并归蓝桥杯考的不是很多,如果要出的话,可能模型都会差不多,因为不会出太难的题,难题基本上都是贪心、DP之类的,所以好好刷题刷熟练就行了&#xff0…

Qt初识 - 编辑框 | 按钮 | 命名规范

目录 一、编辑框 (一) Designer中的编辑框 (二) Code中的编辑框 二、按钮 (一) Designer中的按钮 (二) Code中的按钮 三、Qt中的命名规范 一、编辑框 (一) Designer中的编辑框 进入到Designer界面中 找到Input Widgets目录 找到该目录下的 将这个控件拉出去 双击就可…

STM32CubeIDE基础学习-代码编译介绍

STM32CubeIDE基础学习-代码的编译介绍 前言 当写完代码后,即在调试和下载代码之前都是需要对工程代码进行编译的操作,不然是无法正常进行代码调试和下载的,所以编译这一步是一个关键步骤。 下面就来介绍下STM32CubeIDE软件环境的代码编译方…

用python写一个自动进程守护,带UI

功能是指定程序关闭后自动重启,并点击1作为启动 原来的想法是群成员说的某软件打包后,软件进程被杀后,界面白屏。所以写了个计算器重启demo进行进程守护 import subprocess import time import pyautogui import psutil #用计算器做演示。 d…

数据库压力测试方法概述

一、前言 在前面的压力测试过程中,主要关注的是对接口以及服务器硬件性能进行压力测试,评估请求接口和硬件性能对服务的影响。但是对于多数Web应用来说,整个系统的瓶颈在于数据库。 原因很简单:Web应用中的其他因素,…

【Kafka系列 07】Kafka 如何保证消息不丢失

一、Kafka 消息不丢失的边界 一直以来,很多人对于 Kafka 丢失消息这件事情都有着自己的理解,因而也就有着自己的解决之道。在讨论具体的应对方法之前,我觉得我们首先要明确,在 Kafka 的世界里什么才算是消息丢失,或者…

IQmath库移植至ST系列单片机实战教程1

使用注意事项: 1.注意IQmath库使用的数据范围,如果使用IQ24格式,其范围不能超过-128~128; 如果输入的时候不注意其使用范围,会导致数据溢出,出现一直为0的情况。 如定义 _iq24 a 0; a _IQ24(380)其结果…

史上最细,接口自动化测试用例设计编写总结,一篇带你打通...

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 说到自动化测试&a…

[MYSQL]当数据库被攻破如何重新恢复

前情提要:mysql数据库默认密码、默认端口没有改,也没做安全防护,导致被攻破被索要比特币。 那我们自然是不能给他们的,下面罗列我的补救方法。 密码修改相关 第一步大家自然都会想到先去修改密码: mysqladmin -u roo…

【C语言】操作符相关知识点

移位操作符 << 左移操作符 >>右移操作符 左移操作符 移位规则&#xff1a; 左边抛弃、右边补0 右移操作符 移位规则&#xff1a; 首先右移运算分两种&#xff1a; 1.逻辑移位 左边用0填充&#xff0c;右边丢弃 2.算术移位 左边用原该值的符号位填充&#xff0c;…

ES分布式搜索-IK分词器

ES分词器-IK 1、为什么使用分词器&#xff1f; es在创建倒排索引时需要对文档分词&#xff1b;在搜索时&#xff0c;需要对用户输入内容分词。但默认的分词规则对中文处理并不友好。 我们在kibana的DevTools中测试&#xff1a; GET /_analyze {"analyzer": "…

最简k8s部署(AWS Load Balancer Controller使用)

问题 我需要在k8s集群里面部署springboot服务&#xff0c;通过k8s ingress访问集群内部的springboot服务&#xff0c;应该怎么做&#xff1f; 这里假设已经准备好k8s集群&#xff0c;而且也准备好springboot服务的运行镜像了。这里我们将精力放在k8s服务编排上面。 一图胜千言…

Supplementary Influence Maximization Problem in Social Networks

本论文发表于 IEEE TRANSACTIONS ON COMPUTATIONAL SOCIAL SYSTEMS, VOL. 11, NO. 1, FEBRUARY 2024 Abstract 由于在病毒式营销中的重要应用&#xff0c;影响力最大化&#xff08;IM&#xff09;已成为一个经过充分研究的问题。它的目的是找到一小部分初始用户&#xff0c;以…

智能问数,让数据对话变得如此简单

——用自然语言点亮数据智慧&#xff0c;让深度分析触手可及&#xff0c;让每个人都拥有私人数据分析师。 想象一下&#xff0c;曾经的数据查询&#xff0c;意味着面对着密密麻麻的电子表格&#xff0c;手动筛选、匹配与解读&#xff0c;耗费大量的时间与精力&#xff0c;或者…

【今日面经】24/3/8 又是Java后端面经啊啊啊啊啊啊啊

目录 1.osi七层模型&#xff1f;数据链路层是干什么的&#xff1f;2.tcp三次握手过程&#xff0c;tcp报文头部的结构&#xff1f;里面都有什么&#xff1f;3.讲讲超时重传和快重传&#xff0c;怎么等待的超时重传&#xff08;Timeout Retransmission&#xff09;快速重传&#…

高清数学公式视频素材、科学公式和方程式视频素材下载

适用于科普、解说的自媒体视频剪辑素材&#xff0c;黑色背景数学、科学公式和方程式视频素材下载。 视频编码&#xff1a;H.264 | 分辨率&#xff1a;3840x2160 (4K) | 无需插件 | 文件大小&#xff1a;16.12MB 来自PR视频素材&#xff0c;下载地址&#xff1a;https://prmuban…

Redis持久化机制之RDB内存快照

1、引言 我们经常在数据库层上加一层缓存&#xff08;如Redis&#xff09;&#xff0c;来保证数据的访问效率。 这样性能确实也有了大幅度的提升&#xff0c;因为从内存中取数远比从磁盘中快的多&#xff0c;但是本身Redis也是一层服务&#xff0c;也存在宕机、故障的可能性。…

蓝色经典免费wordpress模板主题

蓝色经典配色的免费wordpress建站主题&#xff0c;万能的wordpress建站主题。 https://www.wpniu.com/themes/24.html

【好书推荐-第十期】《AI绘画教程:Midjourney使用方法与技巧从入门到精通》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公众号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…