Java NIO通信基础示例详解

下面是“Java NIO通信基础示例详解”的完整攻略。

概述

Java NIO是Java 1.4版本引入的一种新的I/O处理方式。相较于传统的I/O方式,NIO采用了非阻塞式I/O模型,使得I/O的效率更高。本文将详细讲解Java NIO通信的基础知识和实现方式。

NIO简介

NIO是New IO的缩写,它是用来替代传统的Java IO的。Java IO(流和相关类)是面向流的,而NIO是面向缓冲区的。NIO提供了与传统IO不同的优点,例如在面向连接的传输方式中(如TCP),NIO可以通过一个单独的线程处理多个连接的输入和输出数据。

NIO的三个核心组件

1. 缓冲区 Buffer

缓冲区是一个对象,可以在其包含的数组中存储数据。Java NIO中的所有缓冲区都是 Buffer 类的子类。 Buffer 类(如下所示)有以下几个核心属性:

  • 容量(Capacity):缓冲区中最多可以存储的数据元素的数量。缓冲区的容量在创建时被固定,并且不能更改。
  • 位置(Position):下一个要读取/写入的元素索引。它默认是0,当数据被写入到缓冲区中时,位置向后移动,当从缓冲区中读取数据时,位置向前移动。
  • 限制(Limit):可以访问和操作的数据索引的下一个位置索引。默认情况下,限制等于容量。
public abstract class Buffer {
    // 容量
    private int capacity;
    // 下一个要读取/写入的元素索引
    private int position = 0;
    // 可以访问和操作的数据索引的下一个位置索引
    private int limit;
    .........
}

2. 通道 Channel

在Java NIO中, Channel是与数据源进行交互的实体。 数据源可以是文件,套接字或服务器端 另一个进程。 Channel的特点是可以被异步地读写。

常用的 Channel 类有:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

3. 选择器 Selector

Selector是Java NIO中的核心组件之一,用于处理多个 Channel的异步和非阻塞IO操作。可以注册到选择器的每个通道都可以被单个线程处理。因此,一个线程可处理多个输入和输出通道。当一个Channel需要进行读写或者连接时,可以使用选择器进行管理。因为在单个线程中,可以管理多个Channel,因此可以提高系统的运行效率。

NIO通信基础示例

示例1:实现简单的文件读写

首先,我们需要先创建一个Buffer对象将数据写入到文件中,然后读出来。

public void fileChannelDemo() throws IOException {
    String content = "hello, Java NIO";
    RandomAccessFile file = new RandomAccessFile("test.txt", "rw");
    FileChannel channel = file.getChannel();
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    // 写入Buffer
    buffer.put(content.getBytes());
    // 缓存区从写模式转为读模式
    buffer.flip();
    // 将buffer中数据写入到通道
    channel.write(buffer);
    buffer.clear();
    // 将数据从通道读取到buffer中
    channel.read(buffer);
    buffer.flip();
    byte[] result = new byte[buffer.remaining()];
    buffer.get(result);
    System.out.println(new String(result));
    file.close();
}

示例2:实现客户端与服务端通信

在这个例子中,我们将实现客户端和服务端通信,客户端将向服务端发送"hello, NIO"字符串,并从服务端接收到"hello, Java NIO"字符串。

服务端部分

public class Server {
    public void server() throws IOException {
        //创建选择器
        Selector selector = Selector.open();
        //创建ServerSocketChannel对象
        ServerSocketChannel server = ServerSocketChannel.open();
        //设置通道为非阻塞状态
        server.configureBlocking(false);
        //设置监听的地址
        server.socket().bind(new InetSocketAddress(9999));
        //将通道注册在选择器上,设置监听事件为:连接(accept)
        server.register(selector, SelectionKey.OP_ACCEPT);
        //循环等待客户端连接
        while(true){
            if(selector.select(1000) == 0){
                continue;
            }
            //获取选择的键集合
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIter = keys.iterator();
            while(keyIter.hasNext()){
                SelectionKey key = keyIter.next();
                //删除已经处理的键信息
                keyIter.remove();
                //处理连接事件(当有客户端连接时)
                if(key.isAcceptable()){
                    //获取相应的ServerSocketChannel
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    //调用accept方法,接收客户端的连接
                    SocketChannel socket = serverChannel.accept();
                    //设置非阻塞状态
                    socket.configureBlocking(false);
                    //在选择器上注册相应的事件:读(read)
                    socket.register(selector, SelectionKey.OP_READ);
                } else if(key.isReadable()){//处理读事件
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int readBytes = socketChannel.read(buffer);
                    if(readBytes > 0){
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String body = new String(bytes, "UTF-8");  
                        System.out.println("Server: Receive Message -> "+ body);
                        String response = "hello, Java NIO";
                        ByteBuffer outBuffer = ByteBuffer.wrap(response.getBytes());
                        socketChannel.write(outBuffer);//将消息回送给客户端
                    }
                }
            }
        }
    }

}

客户端部分

public class Client {
        public void client() throws IOException {
            //创建SocketChannel对象
            SocketChannel socketChannel = SocketChannel.open();
            //设置通道为非阻塞状态
            socketChannel.configureBlocking(false);
            //连接服务端
            socketChannel.connect(new InetSocketAddress("127.0.0.1", 9999));
            Selector selector = Selector.open();
            //将通道注册在选择器上,设置监听事件为:连接(connect)
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while(true){
                if(selector.select(1000) == 0){
                    continue;
                }
                //获取选择的键集合
                Set<SelectionKey> keys = selector.selectedKeys();
                Iterator<SelectionKey> keyIter = keys.iterator();
                while(keyIter.hasNext()){
                    SelectionKey key = keyIter.next();
                    //删除已经处理的键信息
                    keyIter.remove();
                    //连接事件(由于客户端是由连接服务器开始的,因此处理连接事件在这个例子中可有可无)
                    if(key.isConnectable()){
                        SocketChannel channel = (SocketChannel) key.channel();  
                        //判断是否连接成功
                        if(channel.finishConnect()){
                            System.out.println("Client: Connect to server successful!");
                            ByteBuffer buffer = ByteBuffer.wrap("hello, NIO".getBytes());
                            buffer.flip();
                            channel.write(buffer);//向服务器发送消息
                        } else {
                            System.exit(1);
                        }
                    } else if(key.isReadable()){//处理读事件
                        SocketChannel socketChannel1 = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int readBytes = socketChannel1.read(buffer);
                        if(readBytes > 0){
                            buffer.flip();
                            byte[] bytes = new byte[buffer.remaining()];
                            buffer.get(bytes);
                            String body = new String(bytes, "UTF-8");  
                            System.out.println("Client: Receive Message -> " + body);
                        }
                    }
                }
            }
        }
}

上述实例代码实现了Java NIO的基本应用,可以尝试自己写更多的样例来加深理解。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java NIO通信基础示例详解 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Java 中的Printstream介绍_动力节点Java学院整理

    Java 中的PrintStream 介绍 什么是 PrintStream PrintStream 是 Java 中用于输出数据的一个类,提供了一系列的 print() 和 println() 方法实现输出功能。 PrintStream 常用的构造方法有两种: PrintStream(File file) PrintStream(OutputStream o…

    Java 2023年5月26日
    00
  • 超详细的Spring Boot入门笔记(总结)

    下面我就来详细讲解“超详细的SpringBoot入门笔记(总结)”的完整攻略。 一、前言 这篇“超详细的SpringBoot入门笔记(总结)”是一篇针对Java开发人员的入门级教程,主要介绍SpringBoot框架的基础知识、核心原理和应用场景,旨在帮助读者快速掌握SpringBoot的使用和开发。 二、SpringBoot的基础知识 1. SpringBo…

    Java 2023年5月15日
    00
  • 基数排序算法的原理与实现详解(Java/Go/Python/JS/C)

    基数排序算法的原理与实现详解(Java/Go/Python/JS/C) 算法简介 基数排序是一种非比较的排序算法,它通过将数组中的元素从低位到高位依次进行排序,最终实现整个数组的排序。基数排序算法不同于其他排序算法,其不基于比较算法进行排序,因此拥有O(n)的时间复杂度。基数排序算法对于大数据量、高位数的数组排序具有优势。 算法实现 基数排序算法可以使用Ja…

    Java 2023年5月19日
    00
  • springboot log4j2不能打印框架错误日志的解决方案

    我们先来介绍一些概念: Spring Boot:Spring Boot 旨在帮助您创建基于生产的最小限度的应用程序,使用 Spring 平台和第三方库,少量配置的方式,快速启动和运行应用程序。Spring Boot 提供了基础的生产级特性(例如,内嵌 Tomcat,配置管理,或者添加重量级依赖项)。 Log4j2:是目前业界使用广泛的日志框架之一,它提供了丰…

    Java 2023年5月20日
    00
  • Java获取服务器IP及端口的方法实例分析

    Java获取服务器IP及端口的方法实例分析 在Java中获取服务器的IP地址和端口号是很常见的需求。本文将介绍几种Java获取服务器IP及端口的方法实例,通过这些方法可以轻松实现对服务器IP地址和端口的获取。 方法一:使用InetAddress类 我们可以使用Java标准库中的InetAddress类来获取服务器的IP地址和端口号。 import java.…

    Java 2023年6月15日
    00
  • 浅谈java 数据处理(int[][]存储与读取)

    浅谈Java数据处理(int[][]存储与读取) 在Java中,数组是我们常用的数据结构之一。在某些场景下,我们需要处理的数据可能是一个二维数组,本篇文章将会讲解如何处理这种数据结构,包括如何存储和读取。 存储二维数组 Java中的二维数组可以使用 int[][] 来定义,其可以表示一个矩阵。我们可以通过以下代码来定义一个二维数组: int[][] matr…

    Java 2023年5月26日
    00
  • SpringBoot使用JdbcTemplate操作数据库

    SpringBoot使用JdbcTemplate操作数据库攻略 什么是JdbcTemplate JdbcTemplate是Spring Framework中的一个类,它封装了对JDBC的使用,提供了使用非常规范、灵活简单的方式来操作数据库。 使用JdbcTemplate可以避免我们手动编写JDBC代码,使得我们能够更专注于业务逻辑,从而提高效率。 JdbcT…

    Java 2023年5月20日
    00
  • jsp servlet javaBean后台分页实例代码解析

    环境搭建 首先需要安装java开发环境,以及一个支持jsp、servlet开发的IDE,比如Eclipse、IntelliJ IDEA等。接下来创建一个web应用程序,将jsp、servlet等文件放在该应用程序的WEB-INF目录下。 数据库设计 在实现分页之前,需要准备好数据表。这里以用户表为例,设立以下字段信息:id – 用户idname – 用户名a…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部