从BIO到Netty:一步步看懂Java网络编程的迭代逻辑 📅 2026/7/7 4:43:31 一、第一代BIO阻塞IO——最简单但痛点致命1. 什么是BIOBIOBlocking IO也就是阻塞IO是Java最基础、最原始的网络编程方式。它的核心特点是一个连接对应一个线程而且线程在等待客户端连接、等待数据读取/写入时会一直阻塞什么都不做。举个通俗的例子就像一个餐厅每个服务员线程只负责一桌客人客户端连接客人不点单、不吃饭不发数据服务员就一直站在旁边等啥也不干不能去服务其他客人。2. BIO极简DemoJava代码下面是一个最简单的BIO服务器Demo能接收客户端连接并读取消息代码极简重点看“阻塞”的体现import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class BioServer { public static void main(String[] args) throws IOException { // 1. 创建服务器Socket绑定端口8888 ServerSocket serverSocket new ServerSocket(8888); System.out.println(BIO服务器启动等待客户端连接...); while (true) { // 2. 阻塞等待客户端连接关键这里会一直等直到有客户端连进来 Socket clientSocket serverSocket.accept(); System.out.println(有客户端连接 clientSocket.getInetAddress()); // 3. 每个客户端连接开启一个新线程处理一个连接一个线程 new Thread(() - { try { // 4. 读取客户端发送的数据关键没有数据时这里也会阻塞 byte[] buffer new byte[1024]; int len clientSocket.getInputStream().read(buffer); if (len 0) { System.out.println(收到客户端消息 new String(buffer, 0, len)); } // 关闭连接 clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } }).start(); } } }客户端Demo简单测试用import java.io.IOException; import java.net.Socket; import java.io.OutputStream; public class BioClient { public static void main(String[] args) throws IOException { // 连接服务器 Socket socket new Socket(127.0.0.1, 8888); // 发送消息 OutputStream outputStream socket.getOutputStream(); outputStream.write(Hello BIO.getBytes()); // 关闭连接 outputStream.close(); socket.close(); } }3. BIO的核心痛点为什么要迭代BIO的代码简单容易理解但在高并发场景下完全不堪一击核心痛点有3个资源浪费严重一个连接一个线程假设同时有1000个客户端连接就需要1000个线程线程的创建、销毁和切换成本极高服务器内存会被快速耗尽阻塞导致效率极低线程大部分时间都在“等待”等连接、等数据真正处理业务的时间很少相当于“占着茅坑不拉屎”无法支撑高并发面对万级、十万级的客户端连接BIO会直接崩溃根本无法应对比如IM、游戏服务器等场景。正是这些痛点催生了第二代网络编程技术——NIO。二、第二代NIO非阻塞IO——解决阻塞痛点但使用复杂1. 什么是NIONIONon-Blocking IO非阻塞IO是Java 1.4引入的核心目标是解决BIO的阻塞和资源浪费问题。它的核心特点是一个线程可以管理多个客户端连接线程不会一直阻塞没有连接/数据时会去处理其他任务大大提升了资源利用率。还是用餐厅的例子一个服务员线程可以负责多桌客人客户端连接客人不点单时服务员不会一直等而是去服务其他客人只有客人点单有数据时服务员才过来处理。NIO的核心三组件不用记细节知道作用即可Channel通道相当于BIO的Socket是数据传输的通道支持非阻塞Buffer缓冲区数据的容器读取/写入数据都必须通过缓冲区Selector选择器核心组件一个Selector可以监听多个Channel的事件连接、读、写线程通过Selector判断哪个Channel有事件再去处理。2. NIO极简DemoJava代码下面是NIO服务器Demo重点看“一个线程管理多个连接”和“非阻塞”的体现省略复杂异常处理聚焦核心逻辑import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; public class NioServer { public static void main(String[] args) throws IOException { // 1. 创建Selector选择器 Selector selector Selector.open(); // 2. 创建服务器通道绑定端口设置为非阻塞 ServerSocketChannel serverSocketChannel ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8888)); serverSocketChannel.configureBlocking(false); // 3. 将服务器通道注册到Selector监听“连接事件” serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println(NIO服务器启动等待客户端连接...); while (true) { // 4. 阻塞等待事件也可以设置超时时间非永久阻塞 selector.select(); // 5. 获取所有有事件的通道连接、读、写 IteratorSelectionKey iterator selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key iterator.next(); // 移除已处理的事件避免重复处理 iterator.remove(); // 6. 处理连接事件有客户端连接 if (key.isAcceptable()) { ServerSocketChannel serverChannel (ServerSocketChannel) key.channel(); // 接受客户端连接设置为非阻塞 SocketChannel clientChannel serverChannel.accept(); clientChannel.configureBlocking(false); // 将客户端通道注册到Selector监听“读事件” clientChannel.register(selector, SelectionKey.OP_READ); System.out.println(有客户端连接 clientChannel.getInetAddress()); } // 7. 处理读事件客户端发送数据 else if (key.isReadable()) { SocketChannel clientChannel (SocketChannel) key.channel(); // 读取数据到缓冲区 ByteBuffer buffer ByteBuffer.allocate(1024); int len clientChannel.read(buffer); if (len 0) { buffer.flip(); // 切换为读模式 System.out.println(收到客户端消息 new String(buffer.array(), 0, len)); } // 关闭通道 clientChannel.close(); } } } } }客户端可以复用上面的BIO客户端Demo发送消息即可测试。3. NIO解决了BIO的痛点但又带来了新问题优势很明显一个线程可以管理多个连接不用创建大量线程资源利用率大幅提升能支撑更高的并发万级连接解决了BIO的阻塞和资源浪费问题。但NIO的缺点也很突出这也是它没有被广泛直接使用的原因API复杂难用NIO的三组件Channel、Buffer、Selector用法繁琐还要处理缓冲区的flip()、rewind()等操作容易出错存在原生BUG比如著名的“NIO空轮询”问题Selector.select()会一直返回0导致线程空转消耗CPU需要自己处理很多细节比如粘包拆包、断线重连、心跳检测等这些都需要开发者自己实现开发成本高、容易出问题多线程安全问题Buffer是非线程安全的多线程操作时需要自己加锁增加了开发难度。简单说NIO解决了“能不能用”的问题高并发场景但没解决“好不好用”的问题。于是Netty应运而生。三、第三代Netty——基于NIO封装优化开箱即用1. Netty的核心定位对NIO的“封装和优化”一句话总结Netty是一个基于Java NIO的、异步事件驱动的高性能网络通信框架本质上就是对Java原生NIO的“二次封装”解决了NIO的所有痛点让我们能轻松上手高性能网络编程不用再关注底层复杂细节。还是用餐厅的例子Netty相当于一个“专业的餐厅管理系统”它帮你培训服务员优化线程模型、处理客人排队解决空轮询、准备好所有餐具封装API你只需要负责接待客人、提供菜品编写业务逻辑不用再管其他杂事。2. Netty极简DemoJava代码对比NIO的复杂代码Netty的Demo非常简洁不用自己处理Selector、Buffer的复杂操作聚焦业务逻辑即可需要先导入Netty依赖Maven依赖如下!-- Netty核心依赖 -- dependency groupIdio.netty/groupId artifactIdnetty-all/artifactId version4.1.90.Final/version /dependencyNetty服务器Demoimport io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class NettyServer { public static void main(String[] args) throws InterruptedException { // 1. 创建事件循环组相当于NIO的SelectorNetty已封装好 EventLoopGroup bossGroup new NioEventLoopGroup(1); // 处理连接事件 EventLoopGroup workerGroup new NioEventLoopGroup(); // 处理读写事件 try { // 2. 启动器Netty入口封装了NIO的复杂操作 ServerBootstrap bootstrap new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) // 设置服务器通道类型 .childHandler(new ChannelInitializerSocketChannel() { // 3. 配置通道处理器处理业务逻辑 Override protected void initChannel(SocketChannel ch) throws Exception { // 字符串编解码器Netty内置不用自己处理粘包拆包基础逻辑 ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new StringEncoder()); // 自定义业务处理器处理客户端消息 ch.pipeline().addLast(new MyServerHandler()); } }); System.out.println(Netty服务器启动等待客户端连接...); // 4. 绑定端口启动服务器非阻塞 ChannelFuture future bootstrap.bind(8888).sync(); // 5. 等待服务器关闭 future.channel().closeFuture().sync(); } finally { // 关闭事件循环组释放资源 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } // 自定义业务处理器处理客户端消息 static class MyServerHandler extends io.netty.channel.ChannelInboundHandlerAdapter { // 当收到客户端消息时触发 Override public void channelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg) throws Exception { String message (String) msg; System.out.println(收到客户端消息 message); // 回复客户端消息 ctx.writeAndFlush(已收到你的消息 message); } } }Netty客户端Demoimport io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.Scanner; public class NettyClient { public static void main(String[] args) throws InterruptedException { // 1. 创建事件循环组 EventLoopGroup group new NioEventLoopGroup(); try { // 2. 启动器 Bootstrap bootstrap new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializerSocketChannel() { Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new MyClientHandler()); } }); // 3. 连接服务器 ChannelFuture future bootstrap.connect(127.0.0.1, 8888).sync(); // 4. 发送消息从控制台输入 Scanner scanner new Scanner(System.in); while (scanner.hasNextLine()) { String message scanner.nextLine(); future.channel().writeAndFlush(message); } // 5. 等待连接关闭 future.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } // 客户端处理器接收服务器回复 static class MyClientHandler extends io.netty.channel.ChannelInboundHandlerAdapter { Override public void channelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg) throws Exception { String message (String) msg; System.out.println(收到服务器回复 message); } } }3. Netty解决了NIO的所有痛点还带来了额外优势Netty的核心价值就是“解决问题提升体验”具体来说简化开发封装了NIO的复杂API提供了简洁易用的接口比如Bootstrap启动器、Handler处理器不用再手写Selector、Buffer的复杂逻辑修复原生BUG解决了NIO的空轮询问题底层优化了线程模型提升了稳定性内置解决方案自带粘包拆包、编解码、断线重连、心跳检测等功能不用开发者自己实现降低开发成本高性能采用Reactor线程模型、内存池、零拷贝等技术比原生NIO性能更好能轻松支撑万级、十万级并发连接线程安全内置线程安全机制开发者不用自己处理多线程锁的问题。四、总结从BIO到Netty的迭代逻辑核心必记整个Java网络编程的迭代本质上就是“解决痛点、提升效率、降低门槛”的过程用一张表就能看明白技术类型核心痛点待解决问题迭代后的优势核心定位BIO一个连接一个线程、阻塞严重、资源浪费、无法高并发代码简单、容易理解适用于低并发、简单场景已基本淘汰NIOAPI复杂、有空轮询BUG、需自己处理细节、线程不安全一个线程管理多连接、非阻塞、资源利用率高、支持高并发解决高并发问题但开发难度大很少直接使用Netty无解决了NIO的所有痛点API简洁、稳定、高性能、内置多种解决方案、开箱即用生产环境首选适用于高并发、低延迟场景IM、游戏、分布式框架等看到这里你应该能明白Netty不是凭空出现的它是Java网络编程迭代的必然结果。它没有发明新的技术而是把NIO的能力做了极致的封装和优化让普通人也能轻松上手高性能网络编程。