一文理清什么是BIO以及如何使用

 更新时间:2023年10月01日 11:29:13   作者:goyeer  
这篇文章主要介绍了什么是BIO以及如何使用,BIO英文全名是blockingIO,也叫做阻塞IO,是最容易理解、最容易实现的IO工作方式,本文就来通过一些简单的示例为大家讲讲BIO吧,需要的朋友可以参考下

一、BIO概述

BIO(Blocking I/O)是传统java io编程既同步阻塞IO,服务器实现模式为一个连接一个线程。客户端有连接请求时服务器端就会新起一个线程进行处理。当线程空闲时为减少不必要的线程开销,可以通过线程池机制改善。BIO方式适合用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限应用中。

二、BIO工作机制

客户端

  • 通过Socket对象请求与服务端建立连接。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

服务端

  • 通过ServerSocket注册端口。
  • 服务端通过调用accept方法用于监听客户端的Socket请求。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

三、同步阻塞步骤

  • 服务端启动一个ServerSocket
  • 客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通讯。
  • 客户端发出请求后,先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝。
  • 如果有响应,客户端线程会等待请求结束后,在继续执行。

四、编码实现传统BIO

  • 传统的同步阻塞模型开发中,服务端ServerSocket负责绑定IP地址,启动监听端口;客户端Socket负责 发起 连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
  • 基于BIO模式下的通信,客户端-服务端是完全同步,完全藕合的。

服务端代码

 public static void main(String[] args) {
        System.out.println("===服务端启动===");
        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(5000);
            Socket socket=serverSocket.accept();
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String msg;
            if ((msg = br.readLine()) != null) {
                System.out.println("服务端接收客户端信息为:" + msg);
            }
        }catch (Exception exception){
            System.out.println(exception.getMessage());
        }
  }

客户端代码

public static void main(String[] args) {
        Socket socket = null;
        try {
            socket = new Socket("127.0.0.1",5000);
            OutputStream os = socket.getOutputStream();
            PrintStream ps = new PrintStream(os);
            ps.println("Hi BIO! 与服务端通信成功");
            ps.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

总结

传统BIO服务端会一直等待客户端的消息,如果客户端没有进行消息的发送,服务端将一直进入阻塞状态,同时服务端是按照行获取消息的,这意味着客户端也必须按照行进行消息的发送,否则服务端将进 入等待消息的阻塞状态。

五、BIO编程现实多发多收

BIO简单方式是一个接收一个发送,如果实现多发多接收,下面将验证多发多收的代码

服务端代码

public static void main(String[] args) {
    System.out.println("===服务端启动===");
    try {
         ServerSocket ss = new ServerSocket(9988);
         Socket socket = ss.accept();
         InputStream is = socket.getInputStream();
         BufferedReader br = new BufferedReader(new InputStreamReader(is));
         String message;
         while ((message = br.readLine()) != null){
             System.out.println("服务端接收客户端信息为:" + message);
         }
     } catch (IOException e) {
            e.printStackTrace();
     }
}

客户端代码

public static void main(String[] args) {
    try {
        Socket socket = new Socket("localhost",9988);
        OutputStream os = socket.getOutputStream();
        PrintStream ps = new PrintStream(os);
        Scanner scanner = new Scanner(System.in);
        while (true){
            System.out.println("请输入:");
            String input = scanner.nextLine();
            ps.println(input);
            ps.flush();
        }
     } catch (IOException e) {
            e.printStackTrace();
     }
}

六、BIO模拟客户端服务端多对一

前面无论是一收一发,还是多发多收都仅限一个客户端,如何实现一个服务端对应多个客户端哪,这个主要是更改服务端代码

实现步骤

  • 监听tcp端口
  • while循环接收连接
  • 对接收到的连接进行InputStream/OutputStream操作

服务端实现

  public void listen() throws IOException {
        ServerSocket serverSocket = null;
        try {
            log.info("服务启动监听");
            serverSocket = new ServerSocket(9988);
            //循环接收到客户端的连接
            while (true) {
                Socket socket = serverSocket.accept();
                //得到连接后,开启一个线程处理连接
                handleSocket(socket);
            }
        }finally {
            if(serverSocket != null){
                serverSocket.close();
            }
        }
 }
private void handleSocket(Socket socket) {
        HandleSocket socketHandle = new HandleSocket(socket);
        new Thread(socketHandle).start();
}
 public void run() {
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream  = null;
        try {
            bufferedInputStream = new BufferedInputStream(socket.getInputStream());
            byte[] bytes = new byte[1024];
            int len ;
            if ((len = bufferedInputStream.read(bytes)) > -1) {
                String result = new String(bytes,0,len);
                System.out.println("本次接收到的结果:"+result);
            }
            System.out.println("回复信息给客户端:");
            bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
            String outString = Thread.currentThread().getName()+"接收到了";
            bufferedOutputStream.write(outString.getBytes());
            bufferedOutputStream.flush();
            System.out.println("回复完成:");
        } catch (IOException e) {
            System.out.println("异常:"e.getLocalizedMessage());
        } finally {
            System.out.println("关闭数据流:");
            try {
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
                if (bufferedOutputStream != null) {
                    bufferedOutputStream.close();
                }
            }catch (IOException e){
                System.out.println("请输入:"e.getLocalizedMessage());
            }
        }
    }

客户端实现

1.与服务端建立连接

2.发送消息给服务端

3.接收服务端返回的消息

  public void start() throws IOException {
        Socket socket = new Socket("127.0.0.1", 8081);
        String msg = "Hi,This is the BioClient";
        //1.拿到输出流
        //2.对输出流进行处理
       System.out.println("请输入:"+msg);
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = msg.getBytes();
        //3.输出msg
        bufferedOutputStream.write(bytes);
        bufferedOutputStream.flush();
        System.out.println("发送完毕.");
        System.out.println("开始接收到消息.");
        //4.对输入流进行处理
        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        byte[] inBytes = new byte[1024];
        int len;
        //5.读取输入流
        if ((len = bufferedInputStream.read(inBytes)) != -1) {
            String result = new String(inBytes, 0, len);
            System.out.println("接收到的消息="+result);
        }
        //6.关闭输入输出流
        bufferedOutputStream.close();
        bufferedInputStream.close();
        socket.close();
    }

七、总结

  • 每个Socket接收到,都会创建一个线程,线程的竞争、切换上下文影响性能;
  • 每个线程都会占用栈空间和CPU资源;
  • 并不是每个socket都进行lO操作,无意义的线程处理;
  • 客户端的并发访问增加时。服务端将呈现1:1的线程开销,访问量越大,系统将发生线程栈溢出, 线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务;

以上就是一文理清什么是BIO以及如何使用的详细内容,更多关于Java BIO的资料请关注脚本之家其它相关文章!

相关文章

  • 使用java生成激活码和密钥的方法

    使用java生成激活码和密钥的方法

    本文主要介绍了java生成激活码和密钥的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • Mybatis-Plus通过SQL注入器实现批量插入的实践

    Mybatis-Plus通过SQL注入器实现批量插入的实践

    本文主要介绍了Mybatis-Plus通过SQL注入器实现批量插入的实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • Spring Security自定义异常 AccessDeniedHandler不生效解决方法

    Spring Security自定义异常 AccessDeniedHandler不生效解决方法

    本文主要介绍了Spring Security自定义异常 AccessDeniedHandler不生效解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • SpringBoot 如何通过 Profile 实现不同环境下的配置切换

    SpringBoot 如何通过 Profile 实现不同环境下的配置切换

    SpringBoot通过profile实现在不同环境下的配置切换,比如常见的开发环境、测试环境、生产环境,SpringBoot常用配置文件主要有 2 种:properties 文件和yml文件,本文给大家详细介绍SpringBoot 通过 Profile 实现不同环境下的配置切换,感兴趣的朋友一起看看吧
    2022-08-08
  • Java POI读取excel中数值精度损失问题解决

    Java POI读取excel中数值精度损失问题解决

    这篇文章主要介绍了Java POI读取excel中数值精度损失问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 浅谈Mybatis SqlSession执行流程

    浅谈Mybatis SqlSession执行流程

    本文主要介绍了浅谈Mybatis SqlSession执行流程,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2021-07-07
  • 如何使用IDEA2022.1 创建Spring Boot项目

    如何使用IDEA2022.1 创建Spring Boot项目

    这篇文章主要介绍了如何使用IDEA2022.1 创建Spring Boot项目,大家在使用idea开发工具时发现给以往的版本略微的不同,细心的小编在此记录下,需要的朋友可以参考下
    2022-08-08
  • 解决Maven打包只有几十K,运行报错no main manifest attribute问题

    解决Maven打包只有几十K,运行报错no main manifest attribute

    这篇文章主要介绍了解决Maven打包只有几十K,运行报错no main manifest attribute问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Java内部类_动力节点Java学院整理

    Java内部类_动力节点Java学院整理

    内部类是指在一个外部类的内部再定义一个类。下面通过本文给大家java内部类的使用小结,需要的朋友参考下吧
    2017-04-04
  • java调用ffmpeg实现视频转换的方法

    java调用ffmpeg实现视频转换的方法

    这篇文章主要介绍了java调用ffmpeg实现视频转换的方法,较为详细分析了java视频格式转换所需要的步骤及具体实现技巧,需要的朋友可以参考下
    2015-06-06

最新评论