网络编程基础:从BIO到NIO再到AIO(一)

0 阅读3分钟

网络编程基础:从BIO到NIO再到AIO

前言

为什么要学习 BIO、NIO、AIO?

在学习 Netty 之前,先理解 Java 的三种 IO 模型:

  • BIO:最基础的网络编程模型,理解网络通信原理
  • NIO:Netty 的底层实现,理解高性能网络编程
  • AIO:了解异步编程思想,拓展知识面

BIO、NIO、AIO 核心区别总结

总结

模型核心特点通俗比喻
BIO同步阻塞:调用后等待结果,线程被阻塞排队办业务,轮到你之前一直等
NIO同步非阻塞:调用后立即返回,需要轮询检查银行大堂经理,不断巡视哪个窗口有人
AIO异步非阻塞:调用后立即返回,结果通过回调通知外卖送达通知,到了自动打电话

BIO核心概念讲解

一、BIO 的三个角色

 ┌─────────────┐                    ┌─────────────┐
 │   Server    │                    │   Client    │
 │  (服务端)   │◀────── Socket ─────│  (客户端)   │
 └─────────────┘                    └─────────────┘
      角色1          通信工具            角色2

1. Server(服务端)

  • 作用:等待客户端连接,处理客户端请求
  • 比喻:像餐厅,等待客人上门
  • 代码MultiBioServer.java

2. Client(客户端)

  • 作用:主动连接服务端,发送请求
  • 比喻:像客人,主动去餐厅
  • 代码SimpleBioClient.java

3. Socket(通信工具)

  • 作用,是通信工具
  • 作用:连接客户端和服务端,传输数据
  • 比喻:像电话线,连接两个人

二、BIO 是什么?

BIO = Blocking IO(阻塞式输入输出)

核心特点:

 // 1. 阻塞等待连接
 Socket socket = serverSocket.accept();  // 没客户端连接,就一直等(阻塞)
 ​
 // 2. 阻塞读取数据
 String message = reader.readLine();     // 没数据,就一直等(阻塞)

BIO 的作用:

  1. 实现网络通信:让两台电脑能互相发消息
  2. 简单易用:代码直观,适合入门学习
  3. 适合低并发:客户端少的场景(比如内部系统)

三、Socket 详解

Socket 是什么?

Socket = 网络通信的端点

 客户端 Socket                    服务端 Socket
 ┌─────────────┐                ┌─────────────┐
 │ InputStream │◀───── 数据 ─────│OutputStream │
 │             │                │             │
 │OutputStream │────── 数据 ────▶│ InputStream │
 └─────────────┘                └─────────────┘

Socket 的两种类型:

类型作用使用场景
ServerSocket监听端口,接受连接服务端专用
Socket实际的通信通道客户端和服务端都用

四、关键 API 说明

API作用是否阻塞
ServerSocket.accept()等待客户端连接
BufferedReader.readLine()读取一行数据
PrintWriter.println()发送一行数据
Socket.getInputStream()获取输入流
Socket.getOutputStream()获取输出流

五、BIO案例代码

 /**
  * 最简单的 BIO 服务端
  * BIO 核心特点:阻塞式 IO
  * - accept() 会阻塞,等待客户端连接
  * - readLine() 会阻塞,等待客户端发消息
  */
 public class MultiBioServer {
 ​
     public static void main(String[] args) throws IOException {
         ServerSocket serverSocket = new ServerSocket(7397);
         System.out.println("服务端启动,等待客户端连接...");
         // 循环接受客户端连接
         while (true) {
             // 等待客户端连接(阻塞)
             Socket clientSocket = serverSocket.accept();
             System.out.println("新客户端连接:" + clientSocket.getRemoteSocketAddress());
             // 为每个客户端创建一个线程处理
             new Thread(new ClientHandler(clientSocket)).start();
         }
     }
 ​
     /**
      * 客户端处理器(每个客户端一个线程)
      */
     static class ClientHandler implements Runnable {
         private Socket socket;
         public ClientHandler(Socket socket) {
             this.socket = socket;
         }
         @Override
         public void run() {
             try {
                 // 获取输入输出流
                 BufferedReader reader = new BufferedReader(
                     new InputStreamReader(socket.getInputStream(), "UTF-8")
                 );
                 PrintWriter writer = new PrintWriter(
                     new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),
                     true
                 );
                 // 发送欢迎消息
                 writer.println("欢迎连接到 BIO 服务器!");
                 // 循环读取客户端消息
                 String message;
                 while ((message = reader.readLine()) != null) {
                     System.out.println("[" + socket.getRemoteSocketAddress() + "] 说:" + message);
                     // 回复客户端
                     writer.println("服务端已收到:" + message); 
                     // 如果客户端发送 "bye",结束连接
                     if ("bye".equalsIgnoreCase(message)) {
                         System.out.println("[" + socket.getRemoteSocketAddress() + "] 断开连接");
                         break;
                     }
                 }
                 // 关闭资源
                 reader.close(); writer.close(); socket.close();
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }
 ​
 /**
  * 最简单的 BIO 客户端
  * 
  * 功能:连接服务端,发送消息,接收回复
  */
 public class SimpleBioClient {
 ​
     public static void main(String[] args) throws IOException {
         // 1. 连接服务端(IP + 端口)
         Socket socket = new Socket("127.0.0.1", 7397);
         System.out.println("已连接到服务端:" + socket.getRemoteSocketAddress());
         // 2. 获取输入流(读取服务端消息)
         BufferedReader reader = new BufferedReader(
             new InputStreamReader(socket.getInputStream(), "UTF-8")
         );
         // 3. 获取输出流(发送消息给服务端)
         PrintWriter writer = new PrintWriter(
             new OutputStreamWriter(socket.getOutputStream(), "UTF-8"),
             true  // true 表示自动 flush
         );
         // 4. 读取服务端的欢迎消息
         String welcome = reader.readLine();
         System.out.println("服务端说:" + welcome);
         // 5. 从控制台读取用户输入,发送给服务端
         Scanner scanner = new Scanner(System.in);
         System.out.println("请输入消息(输入 bye 退出):");
         while (true) {
             // 读取用户输入
             String input = scanner.nextLine();
             // 发送给服务端
             writer.println(input);
             // 读取服务端回复
             String response = reader.readLine();
             System.out.println("服务端回复:" + response);
             // 如果输入 bye,退出
             if ("bye".equalsIgnoreCase(input)) {
                 break;
             }
         }
         // 6. 关闭资源
         scanner.close(); reader.close();writer.close(); socket.close();
         System.out.println("已断开连接");
     }
 }
 ​