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

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

前言

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

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

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

BIO、NIO、AIO 核心区别总结

总结

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

BIO核心概念讲解

一、BIO 的三个角色

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

1. Server(服务端)

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

2. Client(客户端)

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

3. Socket(通信工具)

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

二、BIO 是什么?

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

核心特点:

ini 复制代码
 // 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案例代码

csharp 复制代码
 /**
  * 最简单的 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();
             }
         }
     }
 }
 ​
java 复制代码
 /**
  * 最简单的 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("已断开连接");
     }
 }
 ​
相关推荐
开源之眼1 小时前
《github star 加星 Taimili.com 艾米莉 》为什么Java里面,Service 层不直接返回 Result 对象?
java·后端·github
心在飞扬1 小时前
RAPTOR 递归文档树优化策略
前端·后端
zone77392 小时前
003:RAG 入门-LangChain 读取图片数据
后端·python·面试
心在飞扬2 小时前
LangChain Parent Document Retriever (父文档检索器)
后端
zone77392 小时前
002:RAG 入门-LangChain 读取文本
后端·算法·面试
用户8356290780512 小时前
在 PowerPoint 中用 Python 添加和定制形状的完整教程
后端·python
武子康2 小时前
大数据-240 离线数仓 - 广告业务 Hive ADS 实战:DataX 将 HDFS 分区表导出到 MySQL
大数据·后端·apache hive
心在飞扬2 小时前
MultiVector 多向量检索
前端·后端
Gopher_HBo2 小时前
Go之基于TCP/IP协议栈的socket通信
后端