【JAVA基础】- BIO介绍及其使用

简介: 【JAVA基础】- BIO介绍及其使用

一、BIO概述

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

二、BIO工作机制

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

三、同步阻塞步骤

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

四、编码实现传统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模拟客户端服务端多对一

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

实现步骤

  1. 监听tcp端口
  2. while循环接收连接
  3. 对接收到的连接进行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的线程开销,访问量越大,系统将发生线程栈溢出, 线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务;
目录
相关文章
|
8月前
|
存储 网络协议 Java
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(二)
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)
92 0
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(二)
|
监控 网络协议 Java
Java 中 IO 之 BIO、NIO 和 AIO
IO 是 Input 和 Output 二词的缩写,意为输入和输出,直接来说,实现一般的 I/O 是没有什么难度的,但涉及到多线程时,要解决 I/O 的问题就不是一个简单的事情了,会涉及到同步和异步的问题,阻塞和非阻塞的问题。了解了(非)同步和(非)阻塞之后,我们再来看 I/O,根据是否同步和是否阻塞以及按它们出现的时间顺序,主要划分为 3 种 I/O 技术,分别是 BIO、NIO 和 AIO。当然,并不是只有这几种,还有其他的 I/O 类型。
105 3
|
5月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
108 2
|
6月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
202 1
|
7月前
|
Java 视频直播 数据库连接
Java I/O 模型详解:BIO、NIO 与 AIO 的特性与应用
Java I/O 模型详解:BIO、NIO 与 AIO 的特性与应用
76 2
|
6月前
|
监控 网络协议 Java
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
93 0
|
7月前
|
存储 监控 Java
深入探索Java BIO与NIO输入输出模型:基于文件复制和socket通信
深入探索Java BIO与NIO输入输出模型:基于文件复制和socket通信
|
8月前
|
编解码 网络协议 Java
用Java的BIO和NIO、Netty实现HTTP服务器(一) BIO与绪论
用Java的BIO和NIO、Netty实现HTTP服务器(一) BIO与绪论
|
8月前
|
网络协议 Java Unix
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(一)
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)
121 0
【Java】BIO源码分析和改造(GraalVM JDK 11.0.19)(一)
|
8月前
|
移动开发 编解码 网络协议
用Java的BIO和NIO、Netty来实现HTTP服务器(三) 用Netty实现
用Java的BIO和NIO、Netty来实现HTTP服务器(三) 用Netty实现