Java NIO初体验

概述

由于BIO(同步阻塞IO)对系统资源的浪费较大。Java1.4中引⼊了NIO框架,在java.nio包中提供了Channel、Selector、Buffer等抽象类,可以快速构建多路复⽤的IO程序,⽤于提供更接近操作系统底层的⾼性能数据操作⽅式。

NIO(Non Blocking IO)是同步⾮阻塞的IO,服务器可以使⽤⼀个线程来处理多个客户端请求,客户端发送的请求会注册到多路复⽤器Selector上,由多路复⽤器Selector轮询各客户端的请求并进⾏处理。

NIO与BIO的比较

  • BIO以流的方式处理数据,而NIO以块的方式处理数据,块IO的效率比流IO高得多

  • BIO是阻塞的,NIO是非阻塞的

  • BIO基于字节流和字符流进行操作,而NIO基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区中写入到通道中。Selector(选择器)用于监听多个通道的事件(比如:连接请求、读写请求等),因此使用单个线程就可以监听多个客户端的通道

NIO可以先将数据写入到缓冲区,然后再由缓冲区写入通道,因此可以做到同步非阻塞

BIO是面向流的,读写数据都是单向的。因此是同步阻塞。

NIO线程模型

NIO包含三⼤组件:

  • Channel通道:每个通道对应⼀个buffer缓冲区

  • Buffer缓冲区:buffer底层是数组,类似于蓄⽔池,channel就是⽔管

  • Selector选择器:selector对应⼀个或多个线程。channel会注册到selector上,由selector根据channel读写事件的发⽣交给某个空闲线程来执⾏。

  • Buffer和Channel都是既可读也可写。

NIO初体验

  • 服务端程序

    package com.my.io.nio;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: NIO服务端
     * @date 2024/1/24 17:23
     */
    public class NIOServer {
    ​
        public static void main(String[] args) throws IOException {
            // 创建ServerSocketChannel对象
            ServerSocketChannel ssc = ServerSocketChannel.open();
            // 设置为非阻塞的方式
            ssc.configureBlocking(false);
            // 设置服务端程序端口
            ssc.socket().bind(new InetSocketAddress(9090));
            // 创建Selector多路复用器
            Selector selector = Selector.open();
            // 把ServerSocketChannel对象注册到Selector上,标识该channel只做客户端连接服务端的事情
            // selectionKey标识唯一的channel
            SelectionKey selectionKey = ssc.register(selector, SelectionKey.OP_ACCEPT);
            
            while (true){
                System.out.println("等待事件发生");
                // 在事件发生之前一直阻塞,轮询监听所有注册到selector上的channel
                int select = selector.select();
                System.out.println("某个事件发生了");
                // 获得所有发生事件的channel
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                // 遍历所有channel
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    // selectionKey==>对应着一个channel
                    SelectionKey key = iterator.next();
                    handle(key);
                    // 删除本次处理的key,防止重复处理
                    iterator.remove();
                }
            }
        }
    ​
        private static void handle(SelectionKey selectionKey) throws IOException {
            // 判断channel上发生了什么事件
            if (selectionKey.isAcceptable()){
                System.out.println("有客户端连接了");
                // 服务端处理客户端的连接,得到ServerSocketChannel,代表着服务端
                ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                // 服务端接收客户端的连接,得到socketChannel,建立起了服务端和客户端具体的连接通道,(阻塞的)
                SocketChannel socketChannel = channel.accept();
                // 把socketChannel设置成非阻塞
                socketChannel.configureBlocking(false);
                // 把socketChannel注册到selector,只处理读事件的发生
                socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
            }else if (selectionKey.isReadable()){
                System.out.println("有客户端向服务端写数据");
                // 获得服务端和客户端之间的通道SocketChannel
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                // 创建Buffer,可用空间为1024B
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 通过socketChannel把数据读到buffer中,返回的是这一次读的字节个数
                // NIO非阻塞的体现,read本身就是非阻塞的,read方法在执行的时刻一定客户端写的操作发生了。
                int len = socketChannel.read(buffer);
                if (len != -1){
                    System.out.println("读到客户端的数据:" + new String(buffer.array(), 0, len));
                }
                // 服务端返回数据给客户端
                ByteBuffer byteBuffer = ByteBuffer.wrap("hello nio".getBytes());
                // 由通道channel去写数据
                socketChannel.write(byteBuffer);
                // 监听下一次事件,读或者写
                selectionKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
    ​
            }
        }
    }
  • 客户端程序

    package com.my.io.nio;
    ​
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Iterator;
    import java.util.Set;
    ​
    /**
     * @author zhupanlin
     * @version 1.0
     * @description: NIO客户端
     * @date 2024/1/24 18:19
     */
    public class NIOClient {
    ​
        public static void main(String[] args) throws IOException {
            // 获得channel通道
            SocketChannel channel = SocketChannel.open();
            // 设置成非阻塞
            channel.configureBlocking(false);
            // 获得Selector
            Selector selector = Selector.open();
            // 客户端连接上服务端
            channel.connect(new InetSocketAddress("127.0.0.1", 9090));
            // 将channel注册到Selector上,并且监听连接事件
            // 由于是客户端主动去连服务端,所以要处理的事件便为连接事件
            channel.register(selector, SelectionKey.OP_CONNECT);
            // 轮询访问selector
            while (true){
                // 阻塞的,在客户端的角度这个方法只面向一个客户端的channel
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()){
                    SelectionKey selectionKey = iterator.next();
                    // 处理任务
                    handle(selectionKey);
                    // 防止重复处理
                    iterator.remove();
                }
            }
        }
    ​
        private static void handle(SelectionKey selectionKey) throws IOException {
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            if (selectionKey.isConnectable()){
                // 如果是正在连接,则完成连接
                if (socketChannel.isConnectionPending()){
                    // finishConnect这个方法执行才表示连接完成了
                    socketChannel.finishConnect();
                    // 设置成非阻塞的模式
                    socketChannel.configureBlocking(false);
                    // 给服务端发消息
                    ByteBuffer byteBuffer = ByteBuffer.wrap("hello server".getBytes());
                    socketChannel.write(byteBuffer);
                    // 获得服务端返回的数据
                    socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
                }
            }else if (selectionKey.isReadable()){
                // 读服务端返回的数据
                // 创建缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 读缓冲区中的数据
                int len = socketChannel.read(buffer);
                if (len != -1){
                    System.out.println("服务端返回的数据:" + new String(buffer.array(), 0, len));
                }
    ​
            }
        }
    ​
    }
  • 通信逻辑

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

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

相关文章

11. 双目视觉之立体视觉基础

目录 1. 深度恢复1.1 单目相机缺少深度信息1.2 如何恢复场景深度&#xff1f;1.3 深度恢复的思路 2. 对极几何约束2.1 直观感受2.2 数学上的描述 1. 深度恢复 1.1 单目相机缺少深度信息 之前学习过相机模型&#xff0c;最经典的就是小孔成像模型。我们知道相机通过小孔成像模…

零基础轻松学编程,中文编程开发工具下载及构件教程

零基础轻松学编程&#xff0c;中文编程开发工具下载及构件教程 一、前言 零基础学编程&#xff0c;中文编程工具下载&#xff0c;中文编程开发工具构件之选择件列表框构件教程&#xff0c; 想学编程可 编程交流.群.一起交流学习&#xff08;关注收藏或点击最下方官网卡片进入…

【c语言】详解操作符(上)

1. 操作符的分类 2. 原码、反码、补码 整数的2进制表示方法有三种&#xff0c;即原码、反码、补码 有符号整数的三种表示方法均有符号位和数值位两部分&#xff0c;2进制序列中&#xff0c;最高位的1位是被当做符号位其余都是数值位。 符号位都是用0表示“正”&#xff0c;用…

设计模式:简介及基本原则

简介 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问&#xff0c;设计模式于己于他人于系统都是多赢的&#xff0c;设计模式使代码编制真正工程化&#xff…

Kotlin快速入门5

Kotlin的继承与重写 kotlin的继承 Kotlin中所有类都继承自Any类&#xff0c;Any类是所有类的超类&#xff0c;对于没有超类型声明的类是默认超类&#xff08;Any 不是 java.lang.Object&#xff09;&#xff1a; class LearnKotlin // 默认继承自Any Any类默认提供三个函数…

Scikit-learn (sklearn)速通 -【莫凡Python学习笔记】

视频教程链接&#xff1a;【莫烦Python】Scikit-learn (sklearn) 优雅地学会机器学习 视频教程代码 scikit-learn官网 莫烦官网学习链接 本人matplotlib、numpy、pandas笔记 1 为什么学习 Scikit learn 也简称 sklearn, 是机器学习领域当中最知名的 python 模块之一. Sk…

IDEA搭建JDK源码学习环境(可添加注释、修改、debug)

工程详见&#xff1a;https://github.com/wenpanwenpan/study-source-jdk1.8.0_281 1、找到src.zip和javafx-src.zip 找到你想要调试的JDK&#xff0c;笔者本地电脑上装了两个版本的JDK&#xff0c;这里以jdk1.8.0_281为例将JDK目录下的javafx-src.zip和src.zip两个压缩包进行…

【GitHub项目推荐--不错的 TypeScript 学习项目】【转载】

在线白板工具 Excalidraw 标星 33k&#xff0c;是一款非常轻量的在线白板工具&#xff0c;可以直接在浏览器打开&#xff0c;轻松绘制具有手绘风格的图形。 如下图所示&#xff0c;Excalidraw 支持最常用的图形元素&#xff1a;方框、圆、菱形、线&#xff0c;可以方便的使用…

【Web】小白也能做的RWCTF体验赛baby题部分wp

遇到不会的题&#xff0c;怎么办&#xff01;有的师傅告诉你完了&#xff0c;废了&#xff0c;寄了&#xff01;只有Z3告诉你&#xff0c;稳辣&#xff01;稳辣&#xff01;都稳辣&#xff01; 这种CVE复现的题型&#xff0c;不可能要求选手从0到1进行0day挖掘&#xff0c;其实…

12.15字符编码(血干JAVA系列)

字符编码 12.15.1 Java常见编码简介12.15.2得到本机的编码显示【例12.68】使用上述方法得到JVM的默认编码 12.15.3乱码产生【例12.69】使用1SO8859-1编码 12.15.1 Java常见编码简介 12.15.2得到本机的编码显示 在 前 面 讲 解 常 用 类 库 的 时 候 曾 经 介 绍 过 &#xff0…

HarmonyOS 沉浸式状态栏实现

比如说 我们代码是这样的 Entry Component struct Index {build() {Column() {Column() {Column() {Text(定标标题).fontSize(20).fontColor(Color.White)}.height(50).justifyContent(FlexAlign.Center)}.width(100%).backgroundColor(#FF0000)}.height(100%)} }你预览器上看…

k8s 版本发布与回滚

一、实验环境准备&#xff1a; kubectl get pods -o wide kubectl get nodes -o wide kubectl get svc 准备两个nginx镜像&#xff0c;版本号一个是V3&#xff0c;一个是V4 二、准备一个nginx.yaml文件 apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deploylab…

AutoGen实战应用(二):多代理协作(Multi-Agent Collaboration)

AutoGen是微软推出的一个全新工具&#xff0c;它用来帮助开发者创建基于大语言模型(LLM)的复杂应用程序. AutoGen能让LLM在复杂工作流程启用多个角色代理来共同协作完成人类提出的任务。在我之前的一篇博客: AutoGen实战应用(一)&#xff1a;代码生成、执行和调试 中我们通过一…

CTF CRYPTO 密码学-6

题目名称&#xff1a;敲击 题目描述&#xff1a; 方方格格&#xff0c;不断敲击 “wdvtdz qsxdr werdzxc esxcfr uygbn” 解题过程&#xff1a; step1&#xff1a;根据题目描述敲击&#xff0c;wdvtdz对应的字符为x step2&#xff1a;依此类推r&#xff0c;z&#xff0c;o&…

使用 create-react-app 创建 react 应用

一、创建项目并启动 第一步&#xff1a;全局安装&#xff1a;npm install -g create-react-app 第二步&#xff1a;切换到想创建项目的目录&#xff0c;使用命令create-react-app hello-react 第三步&#xff1a;进入项目目录&#xff0c;cd hello-react 第四步&#xff1a;启…

Pyecharts 风采:从基础到高级,打造炫酷象形柱状图的完整指南【第40篇—python:象形柱状图】

文章目录 引言安装PyechartsPyecharts象形柱状图参数详解1. Bar 类的基本参数2. 自定义图表样式3. 添加标签和提示框 代码实战&#xff1a;绘制多种炫酷象形柱状图进阶技巧&#xff1a;动态数据更新与交互性1. 动态数据更新2. 交互性设计 拓展应用&#xff1a;结合其他图表类型…

Android 基础技术——列表卡顿问题如何分析解决

笔者希望做一个系列&#xff0c;整理 Android 基础技术&#xff0c;本章是关于列表卡顿问题如何分析解决 onBindViewHolder 优化 是否有耗时操作、重复创建对象、设置监听器、findViewByID、局部的动画对象等操作 是否存在内存泄漏 发生内存泄露&#xff0c;会导致一些不再使用…

工业4.0开放平台通信 统一架构OPC UA的一种测试方法

工业4.0和工业物联网&#xff08;Industrial Internet of Things, IIoT&#xff09;的核心挑战在于设备、机器以及来自不同行业服务之间的安全和标准化的数据和信息交换。 2016年11月工业4.0平台发布了指导纲要《工业4.0产品需要实现哪些准则》&#xff0c;即对于所有位于工业…

概念抽取:构建认知基础的关键步骤

目录 前言1 概念抽取任务定义1.1 概念知识图谱的关系定义1.2 实体与概念的紧密关联1.3 多样的概念关系 2 概念在认知中的重要角色2.1 语言理解的基础2.2 上下位关系的深化理解 3 概念抽取方法3.1 基于模板的抽取3.2 基于百科的抽取3.3 基于机器学习的方法 4 应用4.1 自然语言理…

一行命令在 wsl-ubuntu 中使用 Docker 启动 Windows

在 wsl-ubuntu 中使用 Docker 启动 Windows 0. 背景1. 验证我的系统是否支持 KVM&#xff1f;2. 使用 Docker 启动 Windows3. 访问 Docker 启动的 Windows4. Docker Hub 地址5. Github 地址 0. 背景 我们可以在 Windows 系统使用安装 wsl-ubuntu&#xff0c;今天玩玩在 wsl-ub…