一文理清什么是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的资料请关注脚本之家其它相关文章!

相关文章

  • SpringBoot2整合JTA组件实现多数据源事务管理

    SpringBoot2整合JTA组件实现多数据源事务管理

    这篇文章主要介绍了SpringBoot2整合JTA组件实现多数据源事务管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 详解JVM的内存对象介绍[创建和访问]

    详解JVM的内存对象介绍[创建和访问]

    这篇文章主要介绍了JVM的内存对象介绍[创建和访问],文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • MyBatis-Plus中如何实现动态表名

    MyBatis-Plus中如何实现动态表名

    这篇文章主要介绍了MyBatis-Plus中如何实现动态表名问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • Java泛型的继承和实现操作示例

    Java泛型的继承和实现操作示例

    这篇文章主要介绍了Java泛型的继承和实现操作,结合实例形式分析了java泛型类的继承以及泛型接口的实现相关操作技巧,需要的朋友可以参考下
    2019-08-08
  • netty-grpc一次DirectByteBuffer内存泄露问题

    netty-grpc一次DirectByteBuffer内存泄露问题

    这篇文章主要介绍了netty-grpc一次DirectByteBuffer内存泄露问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Spring Boot 文件上传原理解析

    Spring Boot 文件上传原理解析

    Spring Boot 文件上传原理其实就是Spring MVC,因为这部分工作是Spring MVC做的而不是Spring Boot,那么,SpringMVC又是怎么处理文件上传这个过程的呢?下面通过本文给大家详细介绍下,一起看看吧
    2018-03-03
  • Java实现Fast DFS、服务器、OSS上传功能

    Java实现Fast DFS、服务器、OSS上传功能

    这篇文章主要介绍了Java实现Fast DFS、服务器、OSS上传功能,在实际的业务中,可以根据客户的需求设置不同的文件上传需求,支持普通服务器上传+分布式上传(Fast DFS)+云服务上传OSS(OSS),需要的朋友可以参考下
    2024-04-04
  • 常用的ResponseEntity.BodyBuilder和自定义ResponseEntity的实例

    常用的ResponseEntity.BodyBuilder和自定义ResponseEntity的实例

    这篇文章主要介绍了常用的ResponseEntity.BodyBuilder和自定义ResponseEntity的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • spring boot整合scurity做简单的登录校验的实现

    spring boot整合scurity做简单的登录校验的实现

    这篇文章主要介绍了spring boot整合scurity做简单的登录校验的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-04-04
  • java 代码块与静态代码块加载顺序

    java 代码块与静态代码块加载顺序

    这篇文章主要介绍了java 代码块与静态代码块加载顺序的相关资料,需要的朋友可以参考下
    2017-07-07

最新评论