Netty入门篇二:5分钟从零搭建Netty服务端与客户端(可直接运行,新手零踩坑) 前言上一篇我们从BIO/NIO/AIO底层原理讲清楚了为什么企业清一色选用Netty。理论吃透后本篇直接落地实战带大家从零搭建一套可直接运行的Netty服务端客户端。本文所有代码为企业标准模板代码可直接复用在 IM、网关、RPC、长连接服务中同时逐行拆解Netty启动核心流程解决新手最大痛点会抄代码、不懂流程、报错不会修。全程极简无冗余看完即可跑通、看懂、记住。一、Netty版本选择与Maven依赖1.1 版本选择原则生产环境优先选择稳定长期维护版本避免最新版踩坑、老旧版存在已知BUG这里选用企业最常用的4.1.x 稳定版兼容JDK8适配绝大多数项目。1.2 核心Maven依赖只需引入一个核心依赖包含所有Netty网络编程所需能力编解码、线程模型、事件处理、内存管理等!-- Netty核心依赖 -- dependency groupIdio.netty/groupId artifactIdnetty-all/artifactId version4.1.97.Final/version /dependency补充说明netty-all全量包适合学习、开发、快速搭建项目生产精细化依赖可按需拆分学习阶段直接使用全量包即可该版本修复了空轮询BUG、内存泄漏等经典问题稳定性拉满。二、Netty核心组件快速回顾衔接上篇正式编码前快速回顾5个核心组件所有Netty项目启动逻辑都围绕这5个类ServerBootstrap服务端启动器负责组装整个Netty服务、配置参数、绑定端口Bootstrap客户端启动器负责客户端连接配置、服务端连接发起EventLoopGroup线程池核心分为Boss线程接收连接、Worker线程处理读写ChannelInitializer通道初始化器用于给每个连接绑定自定义处理器、编解码规则ChannelInboundHandlerAdapter业务处理器处理客户端连接、读取数据、异常处理、连接断开等事件。三、实战编写Netty服务端可直接运行3.1 自定义服务端业务处理器负责接收客户端消息、打印日志、响应客户端数据重写核心生命周期方法import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Netty服务端自定义处理器 * 处理连接建立、消息读取、异常、连接断开 */ public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 客户端读取到数据时触发 */ Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // msg即为客户端发送的数据默认是ByteBuf类型 io.netty.buffer.ByteBuf byteBuf (io.netty.buffer.ByteBuf) msg; // 将字节数据转为字符串 String content byteBuf.toString(io.netty.util.CharsetUtil.UTF_8); System.out.println(服务端收到客户端消息 content); // 给客户端响应数据 ctx.writeAndFlush(io.netty.buffer.Unpooled.copiedBuffer(服务端已收到消息 content, io.netty.util.CharsetUtil.UTF_8)); } /** * 通道读取完成后触发 */ Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } /** * 异常捕获 */ Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 打印异常信息 cause.printStackTrace(); // 关闭连接 ctx.close(); } }3.2 服务端启动核心类import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Netty服务端自定义处理器 * 处理连接建立、消息读取、异常、连接断开 */ public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** * 客户端读取到数据时触发 */ Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // msg即为客户端发送的数据默认是ByteBuf类型 io.netty.buffer.ByteBuf byteBuf (io.netty.buffer.ByteBuf) msg; // 将字节数据转为字符串 String content byteBuf.toString(io.netty.util.CharsetUtil.UTF_8); System.out.println(服务端收到客户端消息 content); // 给客户端响应数据 ctx.writeAndFlush(io.netty.buffer.Unpooled.copiedBuffer(服务端已收到消息 content, io.netty.util.CharsetUtil.UTF_8)); } /** * 通道读取完成后触发 */ Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } /** * 异常捕获 */ Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // 打印异常信息 cause.printStackTrace(); // 关闭连接 ctx.close(); } }四、实战编写Netty客户端可直接运行4.1 自定义客户端业务处理器import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; /** * Netty客户端自定义处理器 */ public class NettyClientHandler extends ChannelInboundHandlerAdapter { /** * 客户端连接服务端成功后触发 */ Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // 连接成功后向服务端发送消息 ctx.writeAndFlush(io.netty.buffer.Unpooled.copiedBuffer(Hello Netty, io.netty.util.CharsetUtil.UTF_8)); } /** * 读取服务端响应消息 */ Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { io.netty.buffer.ByteBuf byteBuf (io.netty.buffer.ByteBuf) msg; String content byteBuf.toString(io.netty.util.CharsetUtil.UTF_8); System.out.println(客户端收到服务端响应 content); } /** * 异常处理 */ Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }4.2 客户端启动核心类import 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; /** * Netty客户端启动类 */ public class NettyClient { public static void main(String[] args) throws InterruptedException { // 客户端只需要一个线程池处理连接和读写 EventLoopGroup eventLoopGroup new NioEventLoopGroup(); try { // 客户端启动器 Bootstrap bootstrap new Bootstrap(); bootstrap.group(eventLoopGroup) // 客户端NIO通道 .channel(NioSocketChannel.class) // 初始化处理器 .handler(new ChannelInitializerSocketChannel() { Override protected void initChannel(SocketChannel ch) { ch.pipeline().addLast(new NettyClientHandler()); } }); // 连接服务端 ChannelFuture channelFuture bootstrap.connect(127.0.0.1, 8888).sync(); System.out.println(客户端成功连接Netty服务端); // 监听关闭 channelFuture.channel().closeFuture().sync(); } finally { // 优雅关闭 eventLoopGroup.shutdownGracefully(); } } }五、运行测试 结果演示5.1 运行顺序先启动NettyServer服务监听8888端口再启动NettyClient自动连接服务端并发送消息。5.2 控制台输出结果服务端输出Netty服务端启动成功监听端口8888 服务端收到客户端消息Hello Netty客户端输出客户端成功连接Netty服务端 客户端收到服务端响应服务端已收到消息Hello Netty至此一套完整的Netty双向通信demo跑通六、逐行拆解Netty核心启动流程必懂很多人只会复制代码不懂执行流程这里拆解企业面试必考的Netty启动流程6.1 服务端核心流程创建Boss、Worker 双线程池主从Reactor雏形ServerBootstrap 组装线程池、通道类型、初始化处理器绑定端口启动服务开始监听客户端连接Boss线程接收客户端连接将连接分发到Worker线程Worker线程执行Pipeline中的Handler处理读写、业务逻辑。6.2 客户端核心流程创建单线程池处理所有客户端操作Bootstrap配置通道与处理器主动发起TCP连接连接成功触发channelActive方法完成数据发送与响应接收。七、新手高频踩坑总结避坑指南7.1 端口占用异常问题Bind to port 8888 failed: Address already in use解决关闭占用端口进程或修改监听端口重启项目时确保旧进程完全退出。7.2 客户端连接超时问题无法连接服务端排查优先确认服务端是否启动、IP端口是否正确、防火墙是否拦截。7.3 数据读取乱码/为空原因未指定编码、ByteBuf未正确转换解决统一使用Netty自带UTF_8编码工具类避免原生字符串编码问题。7.4 程序运行结束立即关闭原理closeFuture().sync()阻塞主线程保证服务持续运行缺一不可。八、面试高频考点本篇必背Netty服务端Boss线程和Worker线程的职责区别ServerBootstrap 和 Bootstrap 的区别ChannelInitializer 的作用是什么Netty服务端启动的完整流程是什么为什么Netty线程池要优雅关闭下篇预告下一篇我们将深度拆解Netty核心组件与Pipeline责任链机制彻底搞懂数据在Netty中是如何流转的入站/出站处理器执行顺序是什么为后续讲解TCP粘包拆包打下核心基础。