Java NIO系列教程二

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
可观测可视化 Grafana 版,10个用户账号 1个月
简介: ​ 今天主要是为大家详细的介绍了常见的各种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的具体使用了。

目录
相关文章
|
10天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(二十九)-java+ selenium自动化测试- Actions的相关操作上篇(详解教程)
【4月更文挑战第21天】本文介绍了Selenium中处理特殊测试场景的方法,如鼠标悬停。Selenium的Actions类提供了鼠标悬停功能,用于模拟用户在网页元素上的悬停行为。文中通过实例展示了如何使用Actions悬停并展开下拉菜单,以及在搜索时选择自动补全的字段。代码示例包括了打开百度首页,悬停在“更多”元素上显示下拉菜单并点击“音乐”,以及在搜索框输入关键词并自动补全的过程。
34 0
|
3天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(<https://www.sojump.com/m/2792226.aspx/>)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
12 0
|
3天前
|
Java 测试技术 项目管理
Java基础教程(22)-构建工具Maven的基本使用
【4月更文挑战第22天】Maven是Java项目管理及构建工具,简化构建、测试、打包和部署等任务。遵循约定优于配置原则,核心是`pom.xml`配置文件,用于管理依赖和项目信息。安装涉及下载、解压、配置环境变量。在IDEA中使用Maven创建项目,通过`pom.xml`添加依赖和管理版本。常用命令包括`clean`、`compile`、`test`、`package`、`install`和`deploy`。IDEA支持直接执行这些命令。
|
3天前
|
NoSQL Java 关系型数据库
Java基础教程(21)-Java连接MongoDB
【4月更文挑战第21天】MongoDB是开源的NoSQL数据库,强调高性能和灵活性。Java应用通过MongoDB Java驱动与之交互,涉及MongoClient、MongoDatabase、MongoCollection和Document等组件。连接MongoDB的步骤包括:配置连接字符串、创建MongoClient、选择数据库和集合。伪代码示例展示了如何建立连接、插入和查询数据。
|
4天前
|
存储 前端开发 测试技术
《手把手教你》系列技巧篇(三十五)-java+ selenium自动化测试-单选和多选按钮操作-下篇(详解教程)
【4月更文挑战第27天】本文介绍了使用Java+Selenium进行Web自动化测试时,如何遍历和操作多选按钮的方法。文章分为两个部分,首先是一个本地HTML页面的示例,展示了多选按钮的HTML代码和页面效果,并详细解释了遍历多选按钮的思路:找到所有多选按钮的共同点,通过定位这些元素并放入list容器中,然后使用for循环遍历并操作。 第二部分介绍了在JQueryUI网站上的实战,给出了被测网址,展示了代码设计,同样使用了findElements()方法获取所有多选按钮并存储到list中,然后遍历并进行点击操作。最后,文章对整个过程进行了小结,并推荐了作者的其他自动化测试教程资源。
13 0
|
4天前
|
Java 关系型数据库 MySQL
Java基础教程(20)-Java连接mysql数据库CURD
【4月更文挑战第19天】MySQL是流行的关系型数据库管理系统,支持SQL语法。在IDEA中加载jar包到项目类路径:右击项目,选择“Open Module Settings”,添加库文件。使用JDBC连接MySQL,首先下载JDBC驱动,然后通过`Class.forName()`加载驱动,`DriverManager.getConnection()`建立连接。执行CRUD操作,例如创建表、插入数据和查询,使用`Statement`或`PreparedStatement`,并确保正确关闭数据库资源。
|
5天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
6天前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(三十三)-java+ selenium自动化测试-单选和多选按钮操作-上篇(详解教程)
【4月更文挑战第25天】本文介绍了自动化测试中如何处理单选和多选按钮的操作,包括它们的定义、HTML代码示例以及如何判断和操作这些元素。文章通过一个简单的HTML页面展示了单选和多选框的示例,并提供了Java+Selenium实现的代码示例,演示了如何检查单选框是否选中以及如何进行全选操作。
12 0
|
6天前
|
网络协议 Java 网络架构
Java基础教程(18)-Java中的网络编程
【4月更文挑战第18天】Java网络编程简化了底层协议处理,利用Java标准库接口进行TCP/IP通信。TCP协议提供可靠传输,常用于HTTP、SMTP等协议;UDP协议则更高效但不保证可靠性。在TCP编程中,ServerSocket用于监听客户端连接,Socket实现双进程间通信。UDP编程中,DatagramSocket处理无连接的数据报文。HTTP编程可以通过HttpURLConnection发送请求并接收响应。
|
7天前
|
网络协议 Java Linux
Java基础BIO、NIO、AIO小结(下)
Java基础BIO、NIO、AIO小结(下)
14 0