理解BIO和NIO

K-means算法代码实现(python) K-means算法代码实现以及解决质心选择问题 计算距离 距离通常使用欧几里得距离来衡量 def euclDistance(vector1, vector2): return np.sqrt(sum((vector2 - vector1) ** 2)) 初始化质心 def initCentroids(data, k): numSamples, dim = data.shape ... 阅读详情

一。单线程下的BIO实现

服务端:

public class Server {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024];

        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("服务器已启动并监听8080端口");
            while (true) {
                System.out.println();
                System.out.println("服务器正在等待连接...");
                // (1) 阻塞方法获取新的连接
                Socket socket = serverSocket.accept();

                System.out.println("服务器已接收到连接请求...");
                System.out.println();
                System.out.println("服务器正在等待数据...");
                 // (2) 阻塞读
                  // (3) 按字节流方式读取数据
                socket.getInputStream().read(buffer);
                System.out.println("服务器已经接收到数据");
                System.out.println();

                String content = new String(buffer);
                System.out.println("接收到的数据:" + content);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

客户端:

public class Client {
    public static void main(String[] args) {

        Socket socket = null;
        try {
            socket = new Socket("127.0.0.1",8080);
            String message = "";
            Scanner sc = new Scanner(System.in);
            message = sc.next();
            socket.getOutputStream().write(message.getBytes());
            socket.close();
            sc.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

场景1:启动server端,然后分别启动client1,client2,client3
运行结果:
启动服务端:在这里插入图片描述
然后启动client1 client2 client3,查看服务端控制台日志
在这里插入图片描述
分析结果现象:
如图所示:服务端启动时,并监听端口8080,然后处于阻塞状态,等待客户端连接,当有客户端连接服务端时,服务端接受连接请求,然后等待客户端发送数据。最后客户端1 发送消息,服务端收到消息。从运行结果上来看,说明了服务器端有两个阻塞,一个是等待客户端连接阻塞,另一个就是读客户端数据阻塞。
在这里插入图片描述
单线程下的BIO实现缺点:
1.当服务器端收到客户端连接,但是没有收到客户端的发送数据请求,服务端读客户端数据read()会一直阻塞,如果其他客户端在来请求是无法响应的。

二、多线程下的BIO实现
场景,每次服务器读到客户端发来请求数据就开启一个线程读数据。

public class Server1 {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024];
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            System.out.println("服务器已启动并监听8080端口");
            while (true) {
                System.out.println();
                System.out.println("服务器正在等待连接...");
                // (1) 阻塞方法获取新的连接
                Socket socket = serverSocket.accept();
                // (2) 每一个新的连接都创建一个线程,负责读取数据
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("服务器已接收到连接请求...");
                        System.out.println();
                        System.out.println("服务器正在等待数据...");
                        try {
                            // (3) 按字节流方式读取数据
                            socket.getInputStream().read(buffer);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        System.out.println("服务器已经接收到数据");
                        System.out.println();

                        String content = new String(buffer);
                        System.out.println("接收到的数据:" + content);
                    }
                }).start();

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

缺点:多线程解决了无法并行读取客户端的数据,但是如果大量出现不发送数据的客户端,会导致很多的连接,连接多会导致服务器压力增大。所以如果出现不活跃的线程比较多,应该采用单线程的方案。但是单线程无法处理并发,于是有了NIO。
NIO重要解决客户连接和读取数据阻塞问题的。

三、模拟NIO
1.解决客户端连接和读取数据阻塞问题。

 public class NioServer {
    public static void main(String[] args)  throws InterruptedException{
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            //Java为非阻塞设置的类
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8080));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            while(true) {
                SocketChannel socketChannel = serverSocketChannel.accept();
                if(socketChannel==null) {
                    //表示没人连接
                    System.out.println("正在等待客户端请求连接...");
                    Thread.sleep(5000);
                }else {
                    System.out.println("当前接收到客户端请求连接...");
                }
                if(socketChannel!=null) {
                    //设置为非阻塞
                    socketChannel.configureBlocking(false);
                    byteBuffer.flip();//切换模式  写-->读
                    int effective = socketChannel.read(byteBuffer);
                    if(effective!=0) {
                        String content = Charset.forName("utf-8").decode(byteBuffer).toString();
                        System.out.println(content);
                    }else {
                        System.out.println("当前未收到客户端消息");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

运行结果:
在这里插入图片描述
分析运行结果:
从结果上看,接受到客户端的请求,没有阻塞,但是又回到客户端等待请求,所以会导致丢失当前客户连接请求并丢失客户端发来的消息,怎么办呢?把当前客户请求存储到list上,然后轮询遍历list,验证客户端是否准备好消息,然后打印。如下代码:

public class NioServer1 {
    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        List<SocketChannel> socketList = new ArrayList();

        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(8080));
            //设置非阻塞
            serverSocketChannel.configureBlocking(false);
            while (true){
                SocketChannel socketChannel = serverSocketChannel.accept();
                if(socketChannel == null){
                    System.out.println("正在等待客户连接..");
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    System.out.println("当前收到客户端的连接请求");
                    socketList.add(socketChannel);
                }
                socketList.forEach(s ->{
                        //设置非阻塞
                        try {
                            s.configureBlocking(false);
                            int read = s.read(byteBuffer);
                            if(read != 0){
                                byteBuffer.flip();//切换模式  写-->读
                                String content = Charset.forName("utf-8").decode(byteBuffer).toString();
                                System.out.println(content);
                                byteBuffer.clear();
                            }else{
                                System.out.println("当前未收到客户端消息");
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                });

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

在这里插入图片描述
分析:如图代码所示,我们没有开启第二个线程,使用的是一个线程解决了并发处理客户端请求和发送消息,这样的模式可以很好的解决了BIO在单线程下的并发处理多个客户端的请求的问题,并且解决了非阻塞情况下客户端丢失的问题。
缺点:在接受消息处理上是有问题的,每次来个客户端都要轮询判断数据是否准备好,假设有1000w个连接,甚至更多,采用这种轮询方式效率是低的。
另外,1000w连接中,我们可能只会有100w会有消息,剩下的900w并不会发送任何消息,那么这些连接程序依旧要每次都去轮询,这显然是不合适的。怎么解决呢?将轮询那块代码使用操作系统级别的系统函数(select函数),主动的去感知有数据的socket。
四、真实的NIO

/**
 * NIO 模型中 selector 的作用,
 * 一条连接来了之后,现在不创建一个 while 死循环去监听是否有数据可读了,
 * 而是直接把这条连接注册到 selector 上,
 * 然后,通过检查这个 selector,就可以批量监测出有数据可读的连接,进而读取数据,
 */
public class NioServerSelector {
    public static void main(String[] args) throws IOException{
        //使用selector 解决while的轮询问题,把所有的连接的注册到Selector上
        Selector serverSelector = Selector.open();
        Selector clientSelector = Selector.open();

        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        new Thread(()->{
            try {
                ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
                serverSocketChannel.bind(new InetSocketAddress(8080));
                serverSocketChannel.configureBlocking(false);
                serverSocketChannel.register(serverSelector, SelectionKey.OP_ACCEPT);

                while (true){
                    // 监测是否有新的连接,这里的1指的是阻塞的时间为 1ms
                    if(serverSelector.select(1) > 0){
                        System.out.println("监测是否有新的连接, 当前收到客户端的连接请求..");
                        Set<SelectionKey> selectionKeys = serverSelector.selectedKeys();
                        Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                        while (keyIterator.hasNext()){
                            SelectionKey key = keyIterator.next();
                            if(key.isAcceptable()){
                                try {
                                    // (1) 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector
                                    SocketChannel clientChannel  = ((ServerSocketChannel) key.channel()).accept();
                                    clientChannel.configureBlocking(false);
                                    clientChannel.register(clientSelector, SelectionKey.OP_READ);
                                }finally {
                                    keyIterator.remove();
                                }
                            }
                        }
                    }else {
                        System.out.println("监测是否有新的连接, 正在等待客户端连接..");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()->{
            try {
                ///(2) 批量轮询是否有哪些连接有数据可读,这里的1指的是阻塞的时间为 1ms
                while (true){
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("批量轮询是否有哪些连接有数据可读");
                    if(clientSelector.select(1) > 0){
                        Set<SelectionKey> selectionKeys = clientSelector.selectedKeys();
                        Iterator<SelectionKey> keyIterator  = selectionKeys.iterator();
                        while (keyIterator .hasNext()){
                            SelectionKey key = keyIterator.next();
                            if(key.isReadable()){
                                try {
                                    SocketChannel channel = (SocketChannel)key.channel();
                                    // (3) 面向 Buffer
                                    channel.read(byteBuffer);
                                    byteBuffer.flip();
                                    System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer)
                                            .toString());
                                } catch (IOException e) {
                                    keyIterator.remove();
                                    key.interestOps(SelectionKey.OP_READ);
                                    e.printStackTrace();
                                }
                            }
                        }

                    }

                }
            }catch (Exception e){
                e.printStackTrace();
            }

        }).start();
    }
}
KF-gins输出结果绘图 阅读详情

相关推荐

​开源大模型实战:GLM-4.1V-Thinking本地部署教程(附API调用代码)

​在AI应用从“能用”向“好用”进阶的当下,​的需求愈发强烈:企业需保障数据安全(如医疗影像、金融报告不外传),开发者追求定制化能力(如微调模型适配垂直场景),科研团队则依赖离线环境处理敏感数据。智谱AI开源的​(10B级参数,支持图像、视频、文档多模态推理)凭借的优势,成为本地部署的热门选择。不过,对于“不想折腾技术”的普通用户或快速验证需求的团队而言,​,无需安装配置,打开即用,堪称“零门槛”体验的捷径。本文将分为两部分:先手把手教你​(适合有技术背景的用户),再对比介绍。

gogoMark的博客 1341

关于BIO和NIO的理解

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34381666的博客 597

Python Trimesh库实战:5分钟搞定STL文件解析与3D模型可视化

本文介绍了如何使用Python的Trimesh库快速解析STL文件并进行3D模型可视化。通过简洁的代码示例,演示了从环境搭建、模型加载、几何数据提取到基础变换和批量处理的核心流程,帮助开发者和工程师在5分钟内高效完成3D数据处理与可视化任务。

weixin_32005771的博客 162

BIO与NIO、AIO的区别

     IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。 一、BIO      在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或...

weixin_34301132的博客 9332

手把手教你用GEM-2多频电磁仪找地下管道(附真实数据图)

本文详细解析了使用GEM-2多频电磁仪精准定位地下管线的全流程,包括设备调试、频率选择、现场扫描技巧及数据解读。通过真实案例展示了如何应对复杂干扰环境,提升管线检测效率与准确性,特别适合市政工程和地下管线普查。

weixin_42565652的博客 402

新手避坑指南:在MacBook上为PyTorch正确配置MPS环境,跑通第一个YOLOv8训练

本文详细指导新手如何在MacBook上为PyTorch正确配置MPS环境,并成功运行YOLOv8训练。从环境准备、PyTorch安装到YOLOv8配置和性能调优,提供全面的避坑指南和实用技巧,帮助开发者充分利用Mac的GPU加速能力。

weixin_29100399的博客 230

详解bio与nio

bio和nio

一个草根敲键盘的故事 475

Windows 11 上 WSL2 安装 Ubuntu 22.04 保姆级教程(含桌面环境配置)

本文提供在Windows 11上通过WSL2安装Ubuntu 22.04并配置桌面环境的详细教程。涵盖WSL2核心安装、性能调优、Xfce桌面环境部署以及开发环境集成,帮助开发者构建高效、无缝的Windows-Linux混合开发工作站。

Jerry的专栏 634

从实践角度重新理解BIO和NIO

前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整,但是整个看下来之后,自己对NIO还是一知半解的状态,所以这篇文章不会提到很多的概念,而是站在一个实践的角度,写一些我自己关于NIO的见解,站在实践过后的高度下再回去看概念,应该对概念会有一个更好的理解。 实现一个简易单线程服务器 要讲明白BIO和NIO,首先...

Object的博客 949

手把手教你用Python实现最简单的语义通信(Sem Comm)Demo

本文通过Python和PyTorch,手把手教你构建一个基于深度学习的语义通信(Semantic Communication)Demo。该Demo使用自编码器架构,从MNIST图像中提取低维语义特征进行传输与重建,实现了从“传输比特”到“传递含义”的跨越,并探讨了其效率优势与鲁棒性。

weixin_29080843的博客 247

高德地图+Three.js实战:手把手教你打造数字农场3D可视化大屏(附完整源码)

本文详细介绍了如何利用高德地图JS API与Three.js技术栈,构建一个功能强大的数字农场3D可视化大屏。文章从技术选型、架构设计出发,深入讲解了坐标对齐、图层管理、性能优化等核心工程实践,并提供了完整的模块化代码示例,旨在为智慧农业及类似场景的3D地理可视化项目提供一套可复用的解决方案。

k8s6orchestrator的博客 890

转BIO,NIO和AIO讲的很明白的文章

到底什么是“IO Block” 很多人说BIO不好,会“block”,但到底什么是IO的Block呢?考虑下面两种情况: 用系统调用read从socket里读取一段数据 用系统调用read从一个磁盘文件读取一段数据到内存 如果你的直觉告诉你,这两种都算“Block”,那么很遗憾,你的理解与Linux不同。Linux认为: 对于第一种情况,算作block,因为Linux无法知道网络上对方...

lengyue309的博客 4003

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

对于BIO和NIO的理解

对于NIO和BIO的形象理解和循序渐进的逻辑引导,select,poll和epoll系统调用详解

EDG_Clearlove_7的博客 1290

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

从实践角度理解BIO和NIO

前言 这段时间自己在看一些Java中BIO和NIO之类的东西,看了很多博客,发现各种关于NIO的概念说的天花乱坠头头是道,可以说是非常的完整 但是整个看下来之后,自己对NIO还是一知半解的状态,所以这篇文章不会提到很多的概念,而是站在一个实践的角度,写一些我自己关于NIO的见解 站在实践过后的高度下再回去看概念,应该对概念会有一个更好的理解。 实现一个简易单线程服务器 要讲明白BIO和N...

疯一样的女子 184

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

对NIO和BIO的进一步理解

对NIO非阻塞的进一步理解

敲代码的小小酥的博客 732

BIO、NIO、AIO的简单个人理解,同步异步和阻塞非阻塞的简单理解

如果客户端特别多,几万甚至几百万,服务器端就需要有几万甚至几百万的子线程。由于每个子线程都有自己的独立线程区,这么多子线程可能就产生内存不足等问题。Java IO流是一个庞大的技术体系,其内部提供了很多不同的流, 如: 输入流和输出流,细分下去还有字节流和字符流,甚至还有缓冲流提高 IO 性能,转换流将字节流转换为字符流等等。虽然可以解决一个服务端处理多个客户端,但是因为过多的子线程导致系统资源消耗过多,线程切换导致性能下降也是我们不得不需要考虑的问题。基于TCP/IP实现Socket编程。

2401_84046912的博客 634

谈谈对AIO、BIO和NIO的理解

首先简单理解 BIO,同步阻塞式IO,一个连接一个线程,这个线程只针对这个连接而存在,专注于它的收发,如果没有数据读入它就一直阻塞等待。当然可以通过线程池改善。 AIO,同步非阻塞式IO,一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。  NIO

b7771253的博客 699

从实践角度重新理解 BIO 和 NIO

实现一个简易单线程服务器 要讲明白BIO和NIO,首先我们应该自己实现一个简易的服务器,不用太复杂,单线程即可。 为什么使用单线程作为演示 因为在单线程环境下可以很好地对比出BIO和NIO的一个区别,当然我也会演示在实际环境中BIO的所谓一个请求对应一个线程的状况。 服务端 publicclassServer{ publicstaticvoidmain...

qq_25045173的博客 250

NIO和BIO的对比和理解

BIO 同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会.

felix 542

nio和bio的原理_理解什么是BIO/NIO/AIO

I/O 模型I/O 模型基本说明I/O 模型简单理解为:就是使用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能。Java 支持 3 种网络编程模型:BIO、NIO、AIO。Java BIO:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不作任何事情会造成不必要的线程开销。Java NIO:同步非阻塞...

weixin_39901439的博客 166
上一篇: Linux环境下以命令和脚本方式启动jar包
wflovejava
博客等级 码龄11年 23粉丝 46原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值