一个完整的 Java 网络编程学习项目,涵盖 BIO、NIO、AIO 三种 I/O 模型,以及 NIO Buffer 和回调模式示例
本项目通过实际的代码示例,演示 Java 中三种不同的 I/O 编程模型:
| I/O 模型 | 特点 | 适用场景 |
|---|---|---|
| BIO | 阻塞式,一个连接一个线程 | 连接数较少且固定的场景 |
| NIO | 非阻塞,多路复用 | 连接数多但连接时间短的场景 |
| AIO | 异步非阻塞 | 连接数多且连接时间长的场景 |
network-programming/
├── pom.xml # Maven 配置文件
├── README.md # 项目说明文档
├── LICENSE # 许可证文件
└── src/
└── main/
├── java/ # Java 源代码
│ ├── aio/ # 异步 I/O (AIO)
│ │ ├── Client.java
│ │ └── Server.java
│ ├── bio/ # 阻塞 I/O (BIO)
│ │ ├── tcp/ # TCP 通信
│ │ │ ├── Client.java
│ │ │ ├── MultiThreadServer.java
│ │ │ └── SingleThreadServer.java
│ │ └── udp/ # UDP 通信
│ │ ├── Client.java
│ │ └── Server.java
│ ├── callback/ # 回调模式示例
│ │ ├── AsyncCalculator.java
│ │ ├── Client.java
│ │ └── ResultCallback.java
│ ├── nio/ # 非阻塞 I/O (NIO)
│ │ ├── buffer/ # Buffer 示例
│ │ │ ├── DirectBuffer.java
│ │ │ ├── HeapBuffer.java
│ │ │ ├── MappedBuffer.java
│ │ │ ├── ReadOnlyBuffer.java
│ │ │ └── SubBuffer.java
│ │ ├── other/ # 文件、管道等其他 NIO 示例
│ │ │ ├── NioAsynchronousFileChannel.java
│ │ │ ├── NioFileLock.java
│ │ │ └── NioPipe.java
│ │ ├── tcp/ # TCP 聊天室
│ │ │ ├── Client.java
│ │ │ └── Server.java
│ │ └── udp/ # UDP 通信
│ │ ├── Client.java
│ │ └── Server.java
│ └── util/ # 工具类
│ ├── NetworkConfig.java
│ ├── TCPUtil.java
│ └── UDPUtil.java
└── resources/ # 资源文件
├── image/ # 图片资源
│ ├── 堆内缓冲区.jpeg
│ └── 直接缓冲区.jpeg
├── txt/
│ ├── async-file-channel.txt
│ ├── file-lock.txt
│ └── mapped-buffer.txt
└── logback.xml # 日志配置文件
传统的阻塞式 I/O 模型,每个连接都需要一个独立的线程处理
特点:
运行方式:
# 1. 启动服务端(选择一个)
mvn exec:java -Dexec.mainClass="bio.tcp.SingleThreadServer"
mvn exec:java -Dexec.mainClass="bio.tcp.MultiThreadServer"
# 2. 启动客户端
mvn exec:java -Dexec.mainClass="bio.tcp.Client"
基于 Channel、Buffer、Selector 的非阻塞 I/O 模型
特点:
聊天室示例:
# 1. 启动服务端
mvn exec:java -Dexec.mainClass="nio.tcp.Server"
# 2. 启动多个客户端(分别在不同终端执行同一个客户端类)
mvn exec:java -Dexec.mainClass="nio.tcp.Client"
mvn exec:java -Dexec.mainClass="nio.tcp.Client"
断连行为:
UDP 通信示例:
# 1. 启动服务端
mvn exec:java -Dexec.mainClass="nio.udp.Server"
# 2. 启动客户端
mvn exec:java -Dexec.mainClass="nio.udp.Client"
核心组件:
| 组件 | 说明 |
|:—:|:—:|
| Selector | 多路复用器,用于轮询 Channel 上的事件 |
| Channel | 双向通道,可读可写 |
| Buffer | 数据容器,读写数据的中转站 |
| DatagramChannel | UDP 数据报通道 |
这一组示例聚焦 ByteBuffer 的典型用法和常见陷阱,适合和网络 I/O 一起学习。
| 类名 | 演示重点 |
|---|---|
nio.buffer.HeapBuffer |
展示堆内缓冲区的写入、flip() 切换到读模式后的状态变化 |
nio.buffer.DirectBuffer |
展示直接缓冲区的创建和顺序读取 |
nio.buffer.ReadOnlyBuffer |
展示只读缓冲区与原始缓冲区共享数据,但自身不可写 |
nio.buffer.SubBuffer |
展示 slice() 创建子缓冲区后,底层数据共享带来的联动效果 |
nio.buffer.MappedBuffer |
展示 MappedByteBuffer 将文件映射到内存后直接读取内容 |
适合关注的点:
flip() 负责把缓冲区从写模式切换到读模式slice() 和 asReadOnlyBuffer() 会共享底层数据MappedByteBuffer 更贴近文件 I/O,适合理解零拷贝相关概念这一组示例补充了文件通道、文件锁和管道这几个常见主题,适合和 ByteBuffer、Channel 一起理解。
| 类名 | 演示重点 |
|---|---|
nio.other.NioAsynchronousFileChannel |
展示 AsynchronousFileChannel 的异步读取、异步写入和 CompletionHandler 回调 |
nio.other.NioFileLock |
展示排它锁和共享锁的基本流程,说明文件锁主要用于进程间协作 |
nio.other.NioPipe |
展示 Pipe 在两个线程之间进行单向数据传输 |
适合关注的点:
AsynchronousFileChannel 发起操作后不会阻塞当前线程,通常要等待回调执行完成FileLock 更适合做进程级别的文件访问控制,不适合替代线程锁Pipe 一端负责写入,一端负责读取,适合观察单向数据流转过程JDK 7 引入的异步 I/O,基于回调机制
特点:
运行方式:
# 1. 启动服务端
mvn exec:java -Dexec.mainClass="aio.Server"
# 2. 启动客户端
mvn exec:java -Dexec.mainClass="aio.Client"
核心组件:
| 组件 | 说明 |
|:—:|:—:|
| AsynchronousServerSocketChannel | 异步服务端 Socket 通道 |
| AsynchronousSocketChannel | 异步客户端 Socket 通道 |
| CompletionHandler | 异步操作完成后的回调处理器 |
演示异步编程中的回调模式,实现计算与结果处理的解耦。
文件说明:
| 文件 | 说明 |
|---|---|
AsyncCalculator.java |
异步计算器,执行耗时计算并通过回调返回结果 |
ResultCallback.java |
回调接口,定义计算完成后的处理逻辑 |
Client.java |
使用示例,演示如何使用回调模式 |
public class Client {
static void main(String[] args) {
AsyncCalculator calculator = new AsyncCalculator();
calculator.run(1, 2, result -> System.out.println("计算结果: " + result));
}
}
mvn clean compile
mvn test
# 终端 1 - 启动服务端
mvn exec:java -Dexec.mainClass="bio.tcp.SingleThreadServer"
# 终端 2 - 启动客户端
mvn exec:java -Dexec.mainClass="bio.tcp.Client"
# 终端 1 - 启动服务端
mvn exec:java -Dexec.mainClass="bio.udp.Server"
# 终端 2 - 启动客户端
mvn exec:java -Dexec.mainClass="bio.udp.Client"
# 终端 1 - 启动聊天室服务端
mvn exec:java -Dexec.mainClass="nio.tcp.Server"
# 终端 2 - 启动客户端 1
mvn exec:java -Dexec.mainClass="nio.tcp.Client"
# 终端 3 - 启动客户端 2
mvn exec:java -Dexec.mainClass="nio.tcp.Client"
运行后按提示输入昵称即可;如果你在 IDEA 中运行,记得给 nio.tcp.Client 的运行配置开启“允许多个实例”。
# 堆内缓冲区:观察 position / limit / capacity 的变化
mvn exec:java -Dexec.mainClass="nio.buffer.HeapBuffer"
# 直接缓冲区:演示 allocateDirect 创建的缓冲区读取流程
mvn exec:java -Dexec.mainClass="nio.buffer.DirectBuffer"
# 只读缓冲区:演示共享数据与只读限制
mvn exec:java -Dexec.mainClass="nio.buffer.ReadOnlyBuffer"
# 子缓冲区:演示 slice() 后的共享底层数组
mvn exec:java -Dexec.mainClass="nio.buffer.SubBuffer"
# 内存映射缓冲区:读取 src/main/resources/txt/mapped-buffer.txt
mvn exec:java -Dexec.mainClass="nio.buffer.MappedBuffer"
# 异步文件通道:读取并写入 src/main/resources/txt/async-file-channel.txt
mvn exec:java -Dexec.mainClass="nio.other.NioAsynchronousFileChannel"
# 文件锁:演示排它锁和共享锁访问 src/main/resources/txt/file-lock.txt
mvn exec:java -Dexec.mainClass="nio.other.NioFileLock"
# 管道:演示两个线程之间的单向数据传输
mvn exec:java -Dexec.mainClass="nio.other.NioPipe"
数据存储在 JVM 堆内存中,创建和销毁速度快,但数据复制到 Native 内存时会有性能开销

数据存储在 Native 内存中,避免了数据拷贝,适合频繁的 I/O 操作,但创建和销毁成本较高

| 特性 | BIO | NIO | AIO |
|---|---|---|---|
| 线程模型 | 1:1 (连接:线程) | 1:N (Selector:连接) | 回调驱动 |
| 阻塞性 | 阻塞 | 非阻塞 | 异步非阻塞 |
| API 复杂度 | 简单 | 中等 | 中等 |
| 吞吐量 | 低 | 高 | 高 |
| 延迟 | 高 | 低 | 低 |
| JDK 版本 | 1.0+ | 1.4+ | 1.7+ |
网络配置位于 src/main/java/util/NetworkConfig.java:
public class NetworkConfig {
public static final String HOST = "127.0.0.1"; // 服务端地址
public static final int PORT = 9000; // 服务端端口
public static final int BUFFER_SIZE = 1024; // 缓冲区大小
}
以下示例依赖 src/main/resources/txt 目录下的文本文件,运行时请从项目根目录启动命令:
nio.buffer.MappedBuffer 使用 mapped-buffer.txtnio.other.NioAsynchronousFileChannel 使用 async-file-channel.txtnio.other.NioFileLock 使用 file-lock.txt| 对比项 | BIO | NIO | AIO |
|---|---|---|---|
| 阻塞方式 | 阻塞 | 非阻塞 | 异步非阻塞 |
| 线程模型 | 一连接一线程 | 一线程处理多连接 | 回调驱动 |
| 适用场景 | 连接数少且固定 | 连接数多但时间短 | 连接数多且时间长 |
| 协议 | 特点 | 适用场景 |
|---|---|---|
| TCP | 可靠传输、有序、有连接 | 文件传输、邮件、网页浏览 |
| UDP | 不可靠、无连接、速度快 | 视频直播、DNS、游戏 |
修改 NetworkConfig.java 中的 PORT 值,或终止占用端口的进程:
# Windows 查看端口占用
netstat -ano | findstr :9000
# 终止进程 (PID 为上一步查到的进程ID)
taskkill /PID <进程ID> /F
确保先启动服务端,再启动客户端。NIO 是非阻塞的,客户端连接需要服务端先监听。
| 类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Heap Buffer | 创建快、GC 管理 | I/O 需要复制 | 短生命周期、数据量小 |
| Direct Buffer | 零拷贝、I/O 高效 | 创建慢、需手动释放 | 长生命周期、频繁 I/O |
flip()?因为 ByteBuffer 写入数据后,position 已经移动到末尾;如果不先 flip(),读取时 limit 仍然是容量上限,容易读不到预期内容。
nio.buffer.HeapBuffer 就是专门用来观察这个状态切换的。
欢迎提交 Issue 和 Pull Request
本项目采用 MIT License 开源许可证