Java NIO系列教程二

本文涉及的产品
应用实时监控服务-应用监控,每月50GB免费额度
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: ​ 今天主要是为大家详细的介绍了常见的各种Channel以及他们的用法,本文编写了大量的案例还需要大家认真的去实践以后才能真正的掌握住。介绍完Channel那么下一篇文章我们就可以为大家介绍Buffer和Selector的具体使用了。

NIO详解

一、其他Channel

1. Socket通道

​ 所有的 socket 通道类(DatagramChannel、
SocketChannel 和 ServerSocketChannel)都继承了位于 java.nio.channels.spi 包中的 AbstractSelectableChannel。这意味着我们可以用一个 Selector 对象来执行socket 通道的就绪选择

  • 非阻塞模式中,有更多的伸缩性和灵活性。使用socket,可以用一个线程或者多个线程就可以管理成百上千个socket,管理功能强大,而且没有性能损失,可以通过 Selector监听多个通道
  • DatagramChannel 和 SocketChannel 实现定义读和写功能的接口而ServerSocketChannel 不实现。ServerSocketChannel 负责监听传入的连接和创建新的 SocketChannel 对象,它本身从不传输数据。究其代码是因为它实现的接口比较少
  • socket通道可以被重复使用,socket不可以被重复使用
  • 设置socket的通道模式为阻塞还是非阻塞,可以通过AbstractSelectableChannel.java 中实现的 configureBlocking()方法。传递参数值为 true 则设为阻塞模式,参数值为 false 值设为非阻塞模式

2. ServerSocketChannel

  • 本身不传入数据,主要是为了监听,可以在非阻塞模式下运行没有bind()绑定方法,通过对等的socket来绑定端口并进行监听
  • ServerSocketChannel的对象.socket().bind();
  • 有accept()方法,返回SocketChannel 类型,对象为空则没有链接,反之则。可以在非阻塞下运行

其它 Socket 的 accept()方法会阻塞返回一个 Socket 对象。如果
ServerSocketChannel 以非阻塞模式被调用,当没有传入连接在等待时,ServerSocketChannel.accept( )会立即返回 null。正是这种检查连接而不阻塞的能力实现了可伸缩性并降低了复杂性。可选择性也因此得到实现。我们可以使用一个选择器实例来注册 ServerSocketChannel 对象以实现新连接到达时自动通知的功能

具体的代码思路步骤为:

  • 打开ServerSocketChannel
  • 绑定端口号
  • 设置非阻塞模式configureBlocking(false);
  • 监听连接,通过accept
  • 将其buffer归为0指针rewind(),并且写入buffer
  • 关闭 ServerSocketChannel

具体通过代码演示演示代码代码如下:

public class ServerSocketChannelDemo {
   

    public static void main(String[] args) throws Exception {
   
        //端口号
        int port = 8888;

        //buffer
        ByteBuffer buffer = ByteBuffer.wrap("hello opencoder".getBytes());

        //ServerSocketChannel
        ServerSocketChannel ssc = ServerSocketChannel.open();
        //绑定
        ssc.socket().bind(new InetSocketAddress(port));

        //设置非阻塞模式
        ssc.configureBlocking(false);

        //监听有新链接传入
        while(true) {
   
            System.out.println("Waiting for connections");
            SocketChannel sc = ssc.accept();
            if(sc == null) {
    //没有链接传入
                System.out.println("null");
                Thread.sleep(2000);
            } else {
   
                System.out.println("Incoming connection from: " + sc.socket().getRemoteSocketAddress());
                buffer.rewind(); //指针0
                sc.write(buffer);
                sc.close();
            }
        }
    }
}

通过浏览器点击127.0.0.1:8888,就会出现监听的提示

3. SocketChannel

  • 用于连接到TCP的套接字
  • 实现多路复用
  • 面向缓冲区

主要的特征有:
(1)对于已经存在的 socket 不能创建 SocketChannel
(2)SocketChannel 中提供的 open 接口创建的 Channel 并没有进行网络级联,需要使用 connect 接口连接到指定地址
(3)未进行连接的 SocketChannle 执行 I/O 操作时,会抛出
NotYetConnectedException
(4)SocketChannel 支持两种 I/O 模式:阻塞式和非阻塞式
(5)SocketChannel 支持异步关闭。如果 SocketChannel 在一个线程上 read 阻塞,另一个线程对该 SocketChannel 调用 shutdownInput,则读阻塞的线程将返回-1 表示没有读取任何数据;如果 SocketChannel 在一个线程上 write 阻塞,另一个线程对该SocketChannel 调用 shutdownWrite,则写阻塞的线程将抛出AsynchronousCloseException
(6)SocketChannel 支持设定参数
    SO_SNDBUF 套接字发送缓冲区大小
    SO_RCVBUF 套接字接收缓冲区大小
    SO_KEEPALIVE 保活连接
    O_REUSEADDR 复用地址
    SO_LINGER 有数据传输时延缓关闭 Channel (只有在非阻塞模式下有用)
    TCP_NODELAY 禁用 Nagle 算法

部分代码解释

  • 创建 SocketChannel

      SocketChannel socketChannel = SocketChannel.open(new 
      InetSocketAddress("www.baidu.com", 80));
      //或者
      SocketChannel socketChanne2 = SocketChannel.open();
      socketChanne2.connect(new InetSocketAddress("www.baidu.com", 80));
    
  • 连接校验

      socketChannel.isOpen(); // 测试 SocketChannel 是否为 open 状态
      socketChannel.isConnected(); //测试 SocketChannel 是否已经被连接
      socketChannel.isConnectionPending(); //测试 SocketChannel 是否正在进行
      连接
      socketChannel.finishConnect(); //校验正在进行套接字连接的 SocketChannel
      是否已经完成连接
    
  • 读写模式(有阻塞和非阻塞)

    设置 SocketChannel 的读写模式。false 表示非阻塞,true 表示阻塞

      socketChannel.configureBlocking(false);
    
  • 读写相关代码

      //阻塞读
      SocketChannel socketChannel = SocketChannel.open(
       new InetSocketAddress("www.baidu.com", 80));
      ByteBuffer byteBuffer = ByteBuffer.allocate(16);
      socketChannel.read(byteBuffer);
      socketChannel.close();
      System.out.println("read over");
      //非阻塞读
      SocketChannel socketChannel = SocketChannel.open(
       new InetSocketAddress("www.baidu.com", 80));
      socketChannel.configureBlocking(false);
      ByteBuffer byteBuffer = ByteBuffer.allocate(16);
      socketChannel.read(byteBuffer);
      socketChannel.close();
      System.out.println("read over");
    

完整代码如下:

public class SocketChannelDemo {
   

    public static void main(String[] args) throws Exception {
   
        //创建SocketChannel
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("www.baidu.com", 80));
//        SocketChannel socketChanne2 = SocketChannel.open();
//        socketChanne2.connect(new InetSocketAddress("www.baidu.com", 80));
        //设置阻塞和非阻塞
        socketChannel.configureBlocking(false);
        //读操作
        ByteBuffer byteBuffer = ByteBuffer.allocate(16);
        socketChannel.read(byteBuffer);
        socketChannel.close();
        System.out.println("read over");
    }
}

4. DatagramChannel

  • SocketChannel 对应 Socket,ServerSocketChannel 对应ServerSocket
  • DatagramChannel 对应的是 DatagramSocket 对象,DatagramChannel (如UDP/IP)是无连接的,可以发送单独的数据报给不同的目的地址。同样,可以接收来自任意地址的数据包。

部分代码解释

  • 打开 DatagramChannel

    DatagramChannel server = DatagramChannel.open();
    server.socket().bind(new InetSocketAddress(10086));
    
  • 接收数据

    ByteBuffer receiveBuffer = ByteBuffer.allocate(64);
    receiveBuffer.clear();
    SocketAddress receiveAddr = server.receive(receiveBuffer);
    
  • 发送数据

    DatagramChannel server = DatagramChannel.open();
    ByteBuffer sendBuffer = ByteBuffer.wrap("client send".getBytes());
    server.send(sendBuffer, new InetSocketAddress("127.0.0.1",10086));
    
  • 连接

    read()和 write()只有在 connect()后才能使用,否则会抛异常。

    client.connect(new InetSocketAddress("127.0.0.1",10086));
    int readSize= client.read(sendBuffer);
    server.write(sendBuffer);
    

    具体发送的完整代码

    //发送的实现
    @Test
    public void sendDatagram() throws Exception {
         
        //打开 DatagramChannel
        DatagramChannel sendChannel = DatagramChannel.open();
        InetSocketAddress sendAddress =
                new InetSocketAddress("127.0.0.1",9999);
    
        //发送
        while(true) {
         
            ByteBuffer buffer = ByteBuffer.wrap("发送数据".getBytes("UTF-8"));
            sendChannel.send(buffer,sendAddress);
            System.out.println("已经完成发送");
            Thread.sleep(1000);
        }
    }
    

    具体接收的完整代码

    接收代码的时候其缓冲区一开始要先清除,在将其接收之后缓冲区的数据读写转换在将其输出注意此处的输出语句要用toString()

    //接收的实现
    @Test
    public void receiveDatagram() throws Exception {
         
        //打开DatagramChannel
        DatagramChannel receiveChannel = DatagramChannel.open();
        InetSocketAddress receiveAddress = new InetSocketAddress(9999);
        //绑定
        receiveChannel.bind(receiveAddress);
        //buffer
        ByteBuffer receiveBuffer = ByteBuffer.allocate(1024);
        //接收
        while(true) {
         
            receiveBuffer.clear();
            SocketAddress socketAddress = receiveChannel.receive(receiveBuffer);
            receiveBuffer.flip();
            System.out.println(socketAddress.toString());
            System.out.println(Charset.forName("UTF-8").decode(receiveBuffer));
        }
    }
    

5. Scatter/Gather

以下两种方式都是按照顺序读取或者写入:

​ 分散(scatter)从 Channel 中读取是指在读操作时将读取的数据写入多个 buffer中。

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

​ 聚集(gather)写入 Channel 是指在写操作时将多个 buffer 的数据写入同一个Channel

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = {
    header, body };
channel.write(bufferArray);

他们的使用方式跟其他的Channel基本都是一样的这里就不在赘述了,大家可以按照之前的使用方式自行学一下这两种方式的使用。

总结

​ 今天主要是为大家详细的介绍了常见的各种Channel以及他们的用法,本文编写了大量的案例还需要大家认真的去实践以后才能真正的掌握住。介绍完Channel那么下一篇文章我们就可以为大家介绍Buffer和Selector的具体使用了。

目录
相关文章
|
6天前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
145 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
27天前
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
28天前
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
47 4
|
28天前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
52 1
|
2月前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
1331 1
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
2月前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
181 26
|
28天前
|
前端开发 Java 开发工具
Git使用教程-将idea本地Java等文件配置到gitte上【保姆级教程】
本内容详细介绍了使用Git进行版本控制的全过程,涵盖从本地仓库创建到远程仓库配置,以及最终推送代码至远程仓库的步骤。
38 0
|
2月前
|
监控 Java API
探索Java NIO:究竟在哪些领域能大显身手?揭秘原理、应用场景与官方示例代码
Java NIO(New IO)自Java SE 1.4引入,提供比传统IO更高效、灵活的操作,支持非阻塞IO和选择器特性,适用于高并发、高吞吐量场景。NIO的核心概念包括通道(Channel)、缓冲区(Buffer)和选择器(Selector),能实现多路复用和异步操作。其应用场景涵盖网络通信、文件操作、进程间通信及数据库操作等。NIO的优势在于提高并发性和性能,简化编程;但学习成本较高,且与传统IO存在不兼容性。尽管如此,NIO在构建高性能框架如Netty、Mina和Jetty中仍广泛应用。
56 3
|
2月前
|
存储 监控 Java
Java的NIO体系
通过本文的介绍,希望您能够深入理解Java NIO体系的核心组件、工作原理及其在高性能应用中的实际应用,并能够在实际开发中灵活运用这些知识,构建高效的Java应用程序。
63 5
|
2月前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)