代码分析Java中的BIO与NIO

这篇具有很好参考价值的文章主要介绍了代码分析Java中的BIO与NIO。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开发环境 

OS:Win10(需要开启telnet服务,或使用第三方远程工具)

Java版本:8

BIO

概念

BIO(Block IO),即同步阻塞IO,特点为当客户端发起请求后,在服务端未处理完该请求之前,客户端将一直等待服务端的响应。而服务端在此时也专注于该请求的处理,无法处理其它客户端的请求。

示例

package com.mlyzr.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;

/**
 * @author 勿忘初心
 * @since 2023-08-07-23:59
 * 同步阻塞IO示例
 */
public class BioServer {

    public static void main(String[] args) throws IOException {
        // 监听本地8096端口
        ServerSocket serverSocket = new ServerSocket(8096);
        while(true){
            System.out.println("等待客户端连接...");
            // 接收客户端请求,阻塞
            Socket clientSocket = serverSocket.accept();
            System.out.println("监听到客户端连接");
            // 使用NIO处理请求信息
            handler(clientSocket);
        }
    }

    public static void handler(Socket clientSocket) throws IOException {
        byte[] bytes = new byte[1024];
        System.out.println("准备读取");
        int read = clientSocket.getInputStream().read(bytes);
        System.out.println("读取完毕");
        if(read != -1){
            System.out.println("接收到来自客户端的数据 "+new String(bytes,0,read,StandardCharsets.UTF_8));
        }
    }
}

在IDEA运行上述代码后,将在控制台看如下输出

代码分析Java中的BIO与NIO,java,nio,bio

此时先打开第一个客户端,这里使用telnet连接,具体操作为使用快捷键 Win+R 打开命令行,输入cmd,输入telnet localhost 8096(代码中绑定的端口)后回车,按下 ctrl+] (ctrl+ 右括号)即可进入telnet的交互模式,可向服务端发送信息。(telnet命令报错请使用自行搜索开启telnet服务,或使用其它工具如mobxterm等)

注意:cmd工具的telnet模式下无法发送中文字符,会出现乱码现象,如需发送中文字符请使用MobaXterm等第三方工具进行测试。

代码分析Java中的BIO与NIO,java,nio,bio

 客户端

当客户端成功连接后,服务端控制台输出将由等待状态变为读取状态,等待当前客户端发送消息。

代码分析Java中的BIO与NIO,java,nio,bio

此时再新建一个客户端连接,服务端控制台输出依旧不会发生任何改变, 因为第一个客户端的资源处理还没有结束。

代码分析Java中的BIO与NIO,java,nio,bio

在第一个客户端中使用send命令向服务端发送消息(不熟悉telnet命令的话可以输入help查看)可以看到服务端已经接收到发送的 client1 字符串后输出,并且之前的第二个客户端的连接目前可以被处理,同样进入准备读取的状态。

代码分析Java中的BIO与NIO,java,nio,bio

使用第二个客户端发送消息,此时服务端正常输出。

代码分析Java中的BIO与NIO,java,nio,bio

优化

不难发现上述操作中存在的问题,服务端一次只能处理一个客户端的请求,其余的客户端只能等待,当某一个客户端请求的资源处理耗时较长时,对于其它的客户端使用是非常糟糕的,这里可以通过使用多线程方式来进行优化。
package com.mlyzr.bio;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

/**
 * @author 勿忘初心
 * @since 2023-08-07-23:59
 * 同步阻塞IO示例
 */
public class BioServer {

    public static void main(String[] args) throws IOException {
        // 监听本地8096端口
        ServerSocket serverSocket = new ServerSocket(8096);
        while(true){
            System.out.println("等待客户端连接...");
            // 接收客户端请求,阻塞
            Socket clientSocket = serverSocket.accept();
            System.out.println("监听到客户端连接");

            // 使用多线程来解决系统处理资源的能力
            new Thread(new Runnable(){
                @Override
                public void run() {
                    try {
                        // NIO处理请求资源
                        handler(clientSocket);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
            ).start();
        }


    }

    public static void handler(Socket clientSocket) throws IOException {
        byte[] bytes = new byte[1024];
        System.out.println("准备读取");
        int read = clientSocket.getInputStream().read(bytes);
        System.out.println("读取完毕");
        if(read != -1){
            System.out.println("接收到来自客户端的数据 "+new String(bytes,0,read,StandardCharsets.UTF_8));
        }
    }
}

 使用多线程后,可以看到服务端能够同时监听多个客户端的请求。

代码分析Java中的BIO与NIO,java,nio,bio

总结

使用改进后的代码通过测试可以发现服务端具备了同时处理多个客户端的能力,但同时仍然存在一些问题:

1.当请求非常大时,服务端将因为线程太多无法处理而导致崩溃。

2.即使使用了线程池,当线程池占满后服务将与单线程处理无异。

3.客户端只连接不发送数据,将一直占用服务端资源造成资源浪费。

NIO

概念

同步非阻塞IO,特点为当客户端发起请求后,在此期间客户端可以做其它的操作,但需要主动轮询服务端的处理结果,而且服务端也无需专注于当前请求的处理,也可以处理其他请求。

示例

package com.mlyzr.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @author 勿忘初心
 * @since 2023-08-08-15:28
 * 同步非阻塞IO示例代码
 */
public class NioServer {

    public static void main(String[] args) throws IOException {
        // 保存客户端的Channel集合
        List<SocketChannel> channelList = new ArrayList<>();
        // 创建NIO的ServerSocketChannel,与BIO的ServerSocket类似
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.socket().bind(new InetSocketAddress(8096));
        // 设置channel为非阻塞
        serverChannel.configureBlocking(false);
        System.out.println("等待客户端连接");

        while(true){
            // 非阻塞模式的accept方法不会阻塞
            SocketChannel socketChannel = serverChannel.accept();
            // 客户端是否连接
            if(socketChannel != null){
                System.out.println("监听到客户端连接");
                socketChannel.configureBlocking(false);
                // 将客户端连接保存到list中
                channelList.add(socketChannel);
            }

            Iterator<SocketChannel> iterator = channelList.iterator();
            while(iterator.hasNext()){
                SocketChannel sc = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                int read = sc.read(byteBuffer);
                // 若存在数据,则将数据打印出来
                if(read > 0){
                    System.out.println("接收到客户端数据"+new String(byteBuffer.array(),0,read,StandardCharsets.UTF_8));
                }
                // 无数据则说明客户端已断开
                if(read == -1){
                    iterator.remove();
                    System.out.println("客户端已断开连接");
                }
            }
        }

    }
}

客户端

 运行实例代码后,可以同时打开多个客户端,客户端连接将被加入集合遍历处理,无需等待,服务端此时可以处理多个请求,此时服务端使用的是单线程。

代码分析Java中的BIO与NIO,java,nio,bio

优化

虽然上述代码解决了BIO中遗留的阻塞问题,但多个请求连接而不发送数据占用资源的情况仍然存在,如当请求数据量巨大时,需要通过遍历集合的方式寻找存在需要处理数据的客户端时,时间的损耗仍然非常大。因此使用多路复用的方式来解决无效遍历的问题。

package com.mlyzr.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.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author 勿忘初心
 * @since 2023-08-08-15:28
 * 同步非阻塞IO示例代码
 */
public class NioServer {

    public static void main(String[] args) throws IOException {
        // 创建NIO的ServerSocketChannel,与BIO的ServerSocket类似
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.socket().bind(new InetSocketAddress(8096));
        // 设置channel为非阻塞
        serverChannel.configureBlocking(false);
        // 引入多路复用,提高对Channel的处理能力, 即epoll
        Selector selector = Selector.open();
        // 将ServerSocketChannel注册到selector上,即服务端接收到连接请求时,将连接事件进行注册
        SelectionKey selectionKey = serverChannel.register(selector,SelectionKey.OP_ACCEPT);
        System.out.println("等待客户端连接");

        while(true){
            // 判断是否有注册的事件,无事件将阻塞
            selector.select();

            // 获取所有已经注册的事件实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = selectionKeys.iterator();

            while(keyIterator.hasNext()){
                SelectionKey selectKey = keyIterator.next();
                // 若为连接事件,则获取该实例
                if(selectKey.isAcceptable()){
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectKey.channel();
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    // 设置为非阻塞
                    socketChannel.configureBlocking(false);
                    // 注册读事件,若需要发送消息给客户端可以注册写事件
                    SelectionKey key = socketChannel.register(selector,SelectionKey.OP_READ);
                    System.out.println("监听到客户端连接");
                }
                if (selectKey.isReadable()){
                    SocketChannel socketChannel = (SocketChannel) selectKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int read = socketChannel.read(byteBuffer);
                    // 若存在数据,则将数据打印出来
                    if(read > 0){
                        System.out.println("接收到客户端数据"+new String(byteBuffer.array(),0,read,StandardCharsets.UTF_8));
                    }
                    // 无数据则说明客户端已断开
                    if(read == -1){
                        System.out.println("客户端已断开连接");
                        socketChannel.close();
                    }
                }
                // 从事件集合中删除本次处理的key,防止重复处理
                keyIterator.remove();
            }

        }

    }
}

总结

当使用多路复用后,会监听到客户端的连接事件,并为当前的连接注册读事件,当客户端发送数据时,会被再次监听,此时会进入读操作的事件处理中,将打印接收到的数据,同时每一次的事件使用后将会被移除,防止事件重复,从而解决了无效遍历的问题。文章来源地址https://www.toymoban.com/news/detail-633128.html

到了这里,关于代码分析Java中的BIO与NIO的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

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

    BIO BIO 为 Blocked-IO(阻塞 IO),在 JDK1.4 之前建立网络连接时,只能使用 BIO 使用 BIO 时,服务端会对客户端的每个请求都建立一个线程进行处理,客户端向服务端发送请求后,先咨询服务端是否有线程响应,如果没有就会等待或者被拒绝 BIO 基本使用代码: 服务端: 客户端:

    2024年02月04日
    浏览(29)
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程

    上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就是因其阻塞原因,导致如果想要承受更多的请求就必须有足够多的线程,但是足够多的线程会带来内存占用问题、CPU上下文切换带来的性能问题

    2024年02月14日
    浏览(35)
  • Java NIO FileChannel:BIO与NIO区别、核心组成部分和常用方方法

    深入探讨Java NIO中的FileChannel,包括BIO与NIO的区别、NIO的核心组成部分(Channels、Buffers、Selectors)、FileChannel的常用方法以及示例代码。了解如何使用FileChannel进行文件数据读写操作。

    2024年01月25日
    浏览(39)
  • JAVA的BIO、NIO、AIO模式精解(一)

    在不同系统或进程间数据交互,或高并发场景下都选哟网络通信。早期是基于性能低下的同步阻塞IO(BIO)实现。后支持非阻塞IO(NIO)。 前置须知:javsse,java多线程,javaIO,java网络模型 目的:局域网内通信,多系统间底层消息传递机制,高并发下大数据通信,游戏应用。 IO模型

    2023年04月27日
    浏览(26)
  • JAVA中三种I/O框架——BIO、NIO、AIO

    BIO,同步阻塞IO模型,应用程序发起系统调用后会一直等待数据的请求,直至内核从磁盘获取到数据并拷贝到用户空间; 在一般的场景中,多线程模型下的BIO是成本较低、收益较高的方式。但是,如果在高并发的场景下,过多的创建线程,会严重占据系统资源,降低系统对外

    2024年02月08日
    浏览(42)
  • 【Java基础】BIO/NIO/AIO的详细介绍与比较区分

    BIO 全称 Blocking I/O,它是 JDK 1.4 之前的传统IO模型,是一种同步阻塞的IO,线程发起 IO 后,一直阻塞,直到缓冲区数据就绪后,在进入下一步操作 BIO存在的问题: 无法应对高并发的场景 连接建立后,当前线程没有数据可读就会阻塞,造成资源浪费 BIO适用场景: 客户端连接数

    2024年01月20日
    浏览(26)
  • 从Java BIO到NIO再到多路复用,看这篇就够了

    目录 从一次优化说起 IO模型分类 分类 举例 概念详解 阻塞和非阻塞 同步与异步 Java支持版本 实战 c10k问题 上代码 BIO服务端 NIO服务端​​​​​​​ 多路复用 概念 阶段一:selectpoll 阶段二epoll Java selector 后记         近期优化了一个老的网关系统,在dubbo调用接口rt100

    2024年02月08日
    浏览(33)
  • Java分别用BIO、NIO实现简单的客户端服务器通信

    前言: Java I/O模型发展以及Netty网络模型的设计思想 Java BIO是Java平台上的BIO(Blocking I/O)模型,是Java中用于实现同步阻塞网络编程的一种方式。 在Java中,使用BIO模型需要通过Socket和ServerSocket类来完成网络连接和数据传输,但是由于BIO是同步阻塞的,所以会导致线程阻塞和资

    2024年02月09日
    浏览(31)
  • BIO、NIO线程模型

    BIO(Blocking IO): 同步阻塞模型,一个客户端连接对应一个处理线程,即:一个线程处理一个客户端请求。     单线程版本: 服务端在处理完第一个客户端的所有事件之前,无法为其他客户端提供服务。     多线程版本:如果出现大量只连接不发数据的话,那么就会一直占用

    2024年02月09日
    浏览(29)
  • NIO与BIO

    当谈到 Java 网络编程时,经常会听到两个重要的概念:BIO(Blocking I/O,阻塞 I/O)和 NIO(Non-blocking I/O,非阻塞 I/O)。它们都是 Java 中用于处理 I/O 操作的不同编程模型。 BIO 是 Java 最早的 I/O 模型,也是最简单的一种。在 BIO 模型中,每个 I/O 操作都会阻塞当前线程,直到数据

    2024年04月10日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包