Java网络编程-深入理解BIO、NIO

深入理解BIO与NIO

BIO

BIO 为 Blocked-IO(阻塞 IO),在 JDK1.4 之前建立网络连接时,只能使用 BIO

使用 BIO 时,服务端会对客户端的每个请求都建立一个线程进行处理,客户端向服务端发送请求后,先咨询服务端是否有线程响应,如果没有就会等待或者被拒绝

BIO 基本使用代码:

服务端:

public class TCPServer {
    public static void main(String[] args) throws Exception {
        // 1.创建ServerSocket对象
        System.out.println("服务端 启动....");
        System.out.println("初始化端口 7777 ");
        ServerSocket ss = new ServerSocket(7777); //端口号
        while (true) {
            // 2.监听客户端
            Socket s = ss.accept(); //阻塞
            // 3.从连接中取出输入流来接收消息
            InputStream is = s.getInputStream(); //阻塞
            byte[] b = new byte[10];
            is.read(b);
            String clientIP = s.getInetAddress().getHostAddress();
            System.out.println(clientIP + "说:" + new String(b).trim());
            // 4.从连接中取出输出流并回话
            OutputStream os = s.getOutputStream();
            os.write("服务端回复".getBytes());
            // 5.关闭
            s.close();
        }
    }
}

客户端:

public class TCPClient {
    public static void main(String[] args) throws Exception {
        while (true) {
            // 1.创建Socket对象
            Socket s = new Socket("127.0.0.1", 7777);
            // 2.从连接中取出输出流并发消息
            OutputStream os = s.getOutputStream();
            System.out.println("请输入:");
            Scanner sc = new Scanner(System.in);
            String msg = sc.nextLine();
            os.write(msg.getBytes());
            // 3.从连接中取出输入流并接收回话
            InputStream is = s.getInputStream(); //阻塞
            byte[] b = new byte[20];
            is.read(b);
            System.out.println("客户端发送消息:" + new String(b).trim());
            // 4.关闭
            s.close();
        }
    }
}

BIO 缺点:

  • Server 端会为客户端的每一个连接请求都创建一个新的线程进行处理,如果客户端连接请求数量太多,则会创建大量线程

NIO

从 JDK1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO(即 New IO),NIO 弥补了 BIO 的不足,在服务端不需要为客户端大量的请求而建立大量的处理线程,只需要用很少的线程就可以处理很多客户端请求

NIO 和 BIO 有着相同的目的和作用,但是它们的实现方式完全不同;

  • BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 IO 的效率比流 IO 高很多。
  • NIO 是非阻塞式的,这一点跟 BIO 也很不相同,使用它可以提供非阻塞式的高伸缩性网络。

NIO 有三大核心部分

  • Channel通道
  • Buffer缓冲区
  • Selector选择器

使用 NIO 时,数据是基于 ChannelBuffer 进行操作的,数据从 Channel 被读取到 Buffer 或者相反,Selector 用于监听多个 Channel 通道的事件(连接事件、读写事件),通过 Selector 就可以实现单个线程来监听多个客户端通道

NIO 中的 Channel 用来建立到目标的一个连接,在 BIO 中流是单向的,例如 FileInputStream 只能进行读取操作,而在 NIO 中 Channel 是双向的,既可以读也可以写

NIO 工作流程图如下:Server 端通过单线程来监听多个客户端 Channel 通道中的事件并进行处理

在这里插入图片描述

NIO 使用示例

服务端:

public class NIOServer {
    public static void main(String[] args) throws Exception {
        // 1. 开启一个ServerSocketChannel通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 2. 开启一个Selector选择器
        Selector selector = Selector.open();
        // 3. 绑定端口号8888
        System.out.println("服务端 启动....");
        System.out.println("初始化端口 8888 ");
        serverSocketChannel.bind(new InetSocketAddress(8888));
        // 4. 配置非阻塞方式
        serverSocketChannel.configureBlocking(false);
        // 5. Selector选择器注册ServerSocketChannel通道,绑定连接操作
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        // 6. 循环执行:监听连接事件及读取数据操作
        while (true) {
            // 6.1 监控客户端连接:selecto.select()方法返回的是客户端的通道数,如果为0,则说明没有客户端连接。
            if (selector.select(2000) == 0) {
                System.out.println("服务端等待客户端连接中~");
                continue;
            }
            // 6.2 得到SelectionKey,判断通道里的事件
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            // 遍历所有SelectionKey
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                // 客户端连接请求事件
                if (key.isAcceptable()) {
                    System.out.println("服务端处理客户端连接事件:OP_ACCEPT");
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    socketChannel.configureBlocking(false);
                    // 服务端建立与客户端之间的连接通道 SocketChannel,并且将该通道注册到 Selector 中,监听该通道的读事件
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                // 读取客户端数据事件
                if (key.isReadable()) {
                    // 数据在通道中,先拿到通道
                    SocketChannel channel = (SocketChannel) key.channel();
                    // 取到一个缓冲区,nio读写数据都是基于缓冲区。
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    // 从通道中将客户端发来的数据读到缓冲区
                    channel.read(buffer);
                    System.out.println("客户端数据长度:" + buffer.array().length);
                    System.out.println("客户端发来数据:" + new String(buffer.array()));
                }
                //  6.3 手动从集合中移除当前key,防止重复处理
                keyIterator.remove();
            }
        }
    }
}

客户端:

public class NIOClient {
    public static void main(String[] args) throws Exception {
        // 1. 得到一个网络通道
        SocketChannel channel = SocketChannel.open();
        // 2. 设置非阻塞方式
        channel.configureBlocking(false);
        // 3. 提供服务器端的IP地址和端口号
        InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
        // 4. 连接服务器端,如果用connect()方法连接服务器不成功,则用finishConnect()方法进行连接
        if (!channel.connect(address)) {
            // 因为接需要花时间,所以用while一直去尝试连接。在连接服务端时还可以做别的事,体现非阻塞。
            while (!channel.finishConnect()) {
                // nio 作为非阻塞式的优势,如果服务器没有响应(不启动服务端),客户端不会阻塞,最后会报错,客户端尝试连接服务器连不上。
                System.out.println("客户端等待连接建立时,执行其他任务~");
            }
        }
        // 5. 得到一个缓冲区并存入数据
        String msg = "客户端发送消息:hello";
        ByteBuffer writeBuf = ByteBuffer.wrap(msg.getBytes());
        // 6. 发送数据
        channel.write(writeBuf);
        // 阻止客户端停止,否则服务端也会停止。
        System.in.read();
    }
}

AIO

JDK 7 引入了 Asynchronous IO,即 AIO,叫做异步不阻塞的 IO,也可以叫做 NIO2

在进行 IO 编程中,常用到两种模式:Reactor模式 和 Proactor 模式

  • NIO采用 Reactor 模式,当有事件触发时,服务器端得到通知,进行相应的处理
  • AIO采用 Proactor 模式,引入异步通道的概念,简化了程序编写,一个有效的请求才启动一个线程,它的特点是先由操作系统完成后,才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用

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

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

相关文章

免费!简单优雅的手机视频制作PR模板抖音素材下载

这是一款多功能的Premiere Pro模板&#xff0c;无论你是为视频、宣传内容还是社交媒体帖子短视频&#xff0c;这个pr模板都会为你的项目增添一丝优雅和专业。适用于广播&#xff0c;俱乐部&#xff0c;音乐会&#xff0c;舞蹈&#xff0c;设计&#xff0c;宣传片&#xff0c;动…

什么是前端国际化(internationalization)和本地化(localization)?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

基于QTreeWidget实现带Checkbox的多级组织结构选择树

基于QTreeWidget实现带Checkbox的多级组织结构选择树 采用基于QWidgetMingw实现的原生的组织结构树 通过QTreeWidget控件实现的带Checkbox多级组织结构树。 Qt相关系列文章&#xff1a; 一、Qt实现的聊天画面消息气泡 二、基于QTreeWidget实现多级组织结构 三、基于QTreeWidget…

“新华三杯”第十届成都信息工程大学ACM程序设计竞赛(同步赛)L. 怎么走啊(最短路+二分 分段函数)

题目 登录—专业IT笔试面试备考平台_牛客网 思路来源 衡阳师范学院ac代码、pj学弟 题解 大致可以证明&#xff0c;在w从1e5减小到1的过程中&#xff0c; 之前某条反向边没有用到&#xff0c;现在需要用到反向边&#xff0c;也就是正向边用到的变少了 这样的变化有sqrt个&a…

【Django中间价】项目中常用中间件

原文作者&#xff1a;我辈李想 版权声明&#xff1a;文章原创&#xff0c;转载时请务必加上原文超链接、作者信息和本声明。 文章目录 前言一、常见用法二、JWT中间价三、操作日志中间件1.在user的app下建立models类2.如图位置新建LogMiddleware.py3.django中添加中间件 四、全…

Pandas缺失数据处理

好多数据集都含缺失数据&#xff0c;缺失数据有多重表现形式 数据库中&#xff0c;缺失数据表示为NULL 在某些编程语言中用NA表示 缺失值也可能是空字符串&#xff08;’’&#xff09;或数值 在Pandas中使用NaN表示缺失值&#xff1b; NaN简介 Pandas中的NaN值来自NumPy库&a…

IPQ6010 vs IPQ8072 What’s the difference?|802.11AX WiFi6 Solution DR6018 DR8072

IPQ6010 vs IPQ8072 What’s the difference?|802.11AX WiFi6 Solution DR6018 DR8072 IPQ6010 vs IPQ8072: In-Depth Comparison and Selection Guide The rapid evolution of networking technologies has driven continuous innovation in routers and network devices. Am…

msvcp80.dll文件丢失怎么恢复?详解多种DLL文件修复方法

本文将为您详细介绍msvcp80.dll的定义、作用以及丢失的原因&#xff0c;并提供5个解决方法&#xff0c;帮助您解决这一问题。 一、msvcp80.dll是什么&#xff1f; msvcp80.dll是Microsoft Visual C Runtime Library中的一个动态链接库文件&#xff0c;它包含了许多C运行库函数…

SIM卡内部结构及外部物理接口

1. 内部组成 “SIM卡”是一个装有微处理器的芯片卡&#xff0c;它的内部有5个模块&#xff1a;1.微处理器CPU&#xff1a;控制SIM卡的运算和操作2.程序存储器ROM&#xff1a;存放片内操作系统&#xff0c;用户不可操作。3.工作存储器RAM&#xff1a;存放计算过程中的临时数据4…

Video anomaly detection with spatio-temporal dissociation 论文阅读

Video anomaly detection with spatio-temporal dissociation 摘要1.介绍2.相关工作3. Methods3.1. Overview3.2. Spatial autoencoder3.3. Motion autoencoder3.4. Variance attention module3.5. Clustering3.6. The training objective function 4. Experiments5. Conclusio…

基于Dockerfile创建镜像

Docker镜像的创建 1.基于现有镜像创建 //首先启动一个镜像&#xff0c;在容器里做修改 docker run -itd --name web centos:7 /bin/bash #启动容器docker exec -it web bash #进入容器​ yum install -y epel-release #安装epel源 yum install -y nginx #安装nginx …

共享门店会在未来新零售占据主角吗?

共享门店作为一种创新的商业模式&#xff0c;在未来新零售领域中可能会占据一定的角色&#xff0c;但具体是否会成为主角&#xff0c;还需要根据市场的发展和技术的进步来判断。 首先&#xff0c;共享门店模式通过资源共享、风险共担、客户共享和收益共享等方式&#xff0c;为…

Python 递归及目录遍历

递归调用&#xff1a;一个函数&#xff0c;调用了自身&#xff0c;称为递归调用 递归函数&#xff1a;一个会调用自身的函数 凡是循环能做的事&#xff0c;递归都能做。 目录 递归示例 普通方法实现 递归方式实现 计算分析&#xff1a; 递归遍历目录 引入os 遍历目录 执…

Unity | 渡鸦避难所-2 | 搭建场景并添加碰撞器

1 规范项目结构 上期中在导入一系列的商店资源包后&#xff0c;Assets 目录已经变的混乱不堪 开发过程中&#xff0c;随着资源不断更新&#xff0c;遵循一定的项目结构和设计规范是非常必要的。这可以增加项目的可读性、维护性、扩展性以及提高团队协作效率 这里先做下简单的…

【BigDecimal类—常用API系列】解决java浮点计算精度损失问题

文章目录 Java浮点计算精度损失问题BigDecimal进行精确运算的解决方案 Java浮点计算精度损失问题 BigDecimal它是干什么用的呢&#xff1f;什么是java浮点计算精度损失问题&#xff1f;我们先看一段代码&#xff0c;看这个代码有什么问题&#xff1f;再说BigDeimal这个类是干什…

【机器学习】亚马逊云科技基础知识:以推荐系统为例。你知道机器学习的关键所在么?| 机器学习管道的各个阶段及工作:以Amazon呼叫中心转接问题为例讲解

有的时候,暂时的失利比暂时胜利要好得多。 ————经典网剧《mao pian》,邵半仙儿 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能领域TOP

深入了解—C++11特性

目录 一、 C11简介 二、初始化列表 2.1 C98中{}的初始化问题 2.2 内置类型的列表初始化 2.3 自定义类型的列表初始化 2.3.1. 标准库支持单个对象的列表初始化 2.3.2. 多个对象的列表初始化 三、变量类型推导 3.1 为什么需要类型推导 3.2 decltype类型推导 3.2.1. 推…

方法-PC端远程调试分布式训练

本专栏为深度学习的一些技巧,方法和实验测试,偏向于实际应用,后续不断更新,感兴趣童鞋可关,方便后续推送 简介 一些简单的代码我们使用Pycharm本地调试就能运行成功&#xff0c;但在诸如使用GPU进行分布式训练和推断等场景中&#xff0c;由于我们本地的电脑没有GPU或者没有多…

慢SQL的治理经验

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、慢SQL导致的后果 二、可能导致慢SQL的原因 三、如何发现慢SQL 3.1 JVM Sandbox 四、识别高危SQL 4.1 阿里的重点强制SQL规…