第二章Netty,入门版HelloWorld
一、 服务端代码 HelloWorldServer.java
importio.netty.bootstrap.ServerBootstrap;importio.netty.buffer.Unpooled;importio.netty.channel.*;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioServerSocketChannel;importio.netty.util.CharsetUtil;publicclassHelloWorldServer{publicstaticvoidmain(String[]args)throwsException{EventLoopGroupboss=newNioEventLoopGroup(1);EventLoopGroupworker=newNioEventLoopGroup();try{newServerBootstrap().group(boss,worker).channel(NioServerSocketChannel.class).childHandler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannelch){ch.pipeline().addLast(newChannelInboundHandlerAdapter(){@OverridepublicvoidchannelActive(ChannelHandlerContextctx){System.out.println("Client connected: "+ctx.channel().remoteAddress());// 发送 Hello World 并自动关闭连接ctx.writeAndFlush(Unpooled.copiedBuffer("Hello World!",CharsetUtil.UTF_8)).addListener(ChannelFutureListener.CLOSE);}});}}).bind(8080).sync().channel().closeFuture().sync();}finally{boss.shutdownGracefully();worker.shutdownGracefully();}}}二、 客户端代码 HelloWorldClient.java
importio.netty.bootstrap.Bootstrap;importio.netty.buffer.ByteBuf;importio.netty.channel.*;importio.netty.channel.nio.NioEventLoopGroup;importio.netty.channel.socket.SocketChannel;importio.netty.channel.socket.nio.NioSocketChannel;importio.netty.util.CharsetUtil;publicclassHelloWorldClient{publicstaticvoidmain(String[]args)throwsException{EventLoopGroupgroup=newNioEventLoopGroup();try{newBootstrap().group(group).channel(NioSocketChannel.class).handler(newChannelInitializer<SocketChannel>(){@OverrideprotectedvoidinitChannel(SocketChannelch){ch.pipeline().addLast(newChannelInboundHandlerAdapter(){@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ByteBufin=(ByteBuf)msg;System.out.println("Client received: "+in.toString(CharsetUtil.UTF_8));ctx.close();}@OverridepublicvoidexceptionCaught(ChannelHandlerContextctx,Throwablecause){cause.printStackTrace();ctx.close();}});}}).connect("localhost",8080).sync().channel().closeFuture().sync();}finally{group.shutdownGracefully();}}}三、 运行结果
先运行 HelloWorldServer。
再运行 HelloWorldClient。
客户端输出:Client received: Hello World!
服务端输出:Client connected: /127.0.0.1:xxxxx
四,HelloWorld的详细解释
一、 核心组件解析
1. EventLoopGroup(事件循环组)
作用:Netty 的线程池,负责处理所有的 I/O 操作和事件。
服务端双组模型:
bossGroup:仅负责接收新的客户端连接请求(Accept)。通常设为 1 个线程。
workerGroup:负责已建立连接的读写操作(Read/Write)。默认线程数为 CPU 核数 * 2。
客户端单组模型:只需一个 group,既负责连接也负责读写。
2. Channel(通道)
NioServerSocketChannel:服务端的 ServerSocket 封装,用于监听端口。
NioSocketChannel:客户端或服务端接受连接后的 Socket 封装,代表一条具体的 TCP 连接。
特性:Netty 的 Channel 是异步非阻塞的,所有 I/O 操作都返回 ChannelFuture,允许注册回调监听完成状态。
3. ChannelInitializer(通道初始化器)
作用:当新连接建立时,Netty 会自动调用其 initChannel 方法。
目的:为新创建的 Channel 配置 Pipeline(处理器链),添加必要的 Handler(如编解码器、业务逻辑处理器)。
4. ChannelPipeline & ChannelHandler(管道与处理器)
Pipeline:每个 Channel 都有一个唯一的 Pipeline,它是一个责任链结构。
Handler:链上的节点。
ChannelInboundHandlerAdapter:入站处理器,处理读取数据、连接激活等事件。
channelActive:连接建立成功时触发(常用于发送欢迎语)。
channelRead:接收到数据时触发。
exceptionCaught:发生异常时触发。
二、 执行流程详解
1. 服务端启动流程
创建引导类:ServerBootstrap 实例化。
配置线程组:绑定 boss 和 worker 组。
指定通道类型:设置为 NioServerSocketChannel。
配置子处理器:通过 childHandler 定义新连接的处理逻辑(添加 HelloWorldHandler)。
绑定端口:bind(8080) 启动监听,sync() 阻塞直到绑定成功。
等待关闭:closeFuture().sync() 阻塞主线程,防止程序退出,直到服务端主动关闭。
2. 客户端连接与服务端响应流程
客户端发起连接:Bootstrap.connect(“localhost”, 8080) 发起异步连接。
服务端接收连接:
bossGroup 线程检测到新连接,创建 NioSocketChannel。
将该 Channel 注册到 workerGroup 的某个 EventLoop 上。
触发 ChannelInitializer.initChannel,将自定义 Handler 加入 Pipeline。
触发 channelActive 事件。
服务端发送消息:
在 channelActive 中,调用 ctx.writeAndFlush(“Hello World!”)。
数据经过 Pipeline 出站(Outbound),最终通过 Socket 发送给客户端。
ChannelFutureListener.CLOSE 确保发送完成后立即关闭连接。
客户端接收消息:
客户端 worker 线程检测到数据到达,触发 channelRead。
解析 ByteBuf 并打印内容。
调用 ctx.close() 关闭客户端连接。
三、 关键设计原理
1. Reactor 线程模型
Netty 采用主从 Reactor 多线程模型:
Main Reactor (Boss):单线程处理连接接入,避免竞争锁,提高接入效率。
Sub Reactor (Worker):多线程处理业务 I/O,充分利用多核 CPU 并行处理能力。
2. 零拷贝与 ByteBuf
ByteBuf:Netty 自定义的数据容器,相比 JDK ByteBuffer,它支持自动扩容、堆内/堆外内存混合使用、引用计数管理,避免了频繁的内存分配与回收。
零拷贝:在 writeAndFlush 过程中,Netty 尽可能减少数据在内核态与用户态之间的拷贝次数(如使用 DirectBuffer)。
3. 异步非阻塞 I/O
所有 I/O 操作(connect, read, write)都是异步的,立即返回 ChannelFuture。
开发者可通过 sync() 同步等待结果,或通过 addListener() 注册回调异步处理,极大提升了并发吞吐量。
四、 总结
Netty HelloWorld 虽简单,但完整展示了 Reactor 模型、Pipeline 责任链、异步事件驱动 三大核心机制。它是理解 Netty 高并发能力的基石,后续复杂的协议编解码、心跳检测、流量控制均在此基础上扩展。
需要我为你演示如何在 HelloWorld 基础上添加 StringEncoder/StringDecoder 实现字符串直接传输吗?帮你简化 ByteBuf 的手动转换过程。