Netty基础服务器实现

1. 引入 Maven 依赖

pom.xml 中加入 Netty 依赖:

XML 复制代码
<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.94.Final</version> <!-- 建议使用较新的稳定版本 -->
</dependency>

2. 编写服务端代码 (Server)

Netty 的核心逻辑在于 ChannelInitializer,可以在这里定义处理流程(Pipeline)。

java 复制代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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. 创建两个线程组
        // bossGroup: 专门负责接收客户端连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        // workerGroup: 专门负责处理已连接通道的读写事件
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // 使用 NIO 模式
             .childHandler(new ChannelInitializer<SocketChannel>() {
                 @Override
                 protected void initChannel(SocketChannel ch) {
                     // 2. 配置 Pipeline (数据处理流水线)
                     ChannelPipeline p = ch.pipeline();
                     p.addLast(new StringDecoder()); // 自动将接收到的字节转为 String
                     p.addLast(new StringEncoder()); // 自动将 String 转为字节发送
                     p.addLast(new SimpleChannelInboundHandler<String>() {
                         @Override
                         protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                             System.out.println("收到消息: " + msg);
                             ctx.writeAndFlush("服务端已收到:" + msg); // 回复消息
                         }
                     });
                 }
             });

            // 3. 绑定端口,启动服务
            ChannelFuture f = b.bind(8080).sync();
            System.out.println("服务器启动,监听 8080 端口...");
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

3. 代码核心组件解析

如果想真正"会用" Netty,这 4 个组件是必须理解的:

  1. EventLoopGroup(线程模型)
    • 这就是前面提到的 Reactor 模型。Boss 负责接客,Worker 负责干活。可以简单理解为管理线程的"包工头"。
  2. ChannelInitializer(流水线工厂)
    • 每一个连接建立后,都会执行 initChannel。在这里添加 Handler,就像是给工厂流水线安装不同的处理设备(编码器、解码器、业务逻辑处理器)。
  3. ChannelPipeline(责任链)
    • 所有的 Handler 串在一起组成 Pipeline。数据像水流一样经过每一个 Handler。比如:字节 -> 解码器 -> 业务处理 -> 编码器 -> 字节
  4. ChannelHandler(业务逻辑)
    • 写的业务代码都在这里。例如 channelRead 处理数据输入,exceptionCaught 处理异常。

4. 为什么要这样用?

  • 非阻塞 :上面的 NioEventLoopGroup 确保了即使有成千上万个连接,服务器依然不会因为一个连接处理慢而卡住其他连接。
  • 模块化 :想支持加密?在 Pipeline 里加一个 SslHandler 即可。想支持心跳检测?加一个 IdleStateHandler 即可。完全不需要改动的业务逻辑代码。

进阶建议:

  • 不要在 Handler 中做耗时操作 :Netty 的 EventLoop 线程非常宝贵,如果的业务逻辑(如查数据库)很慢,必须使用 Netty 提供的 EventExecutorGroup 将任务异步化,否则会把整个 Netty 线程池卡死。
  • 粘包/拆包处理 :在真实生产环境,TCP 会发生"粘包"(两条消息连在一起)或"拆包"(一条消息断成两截)。不要直接用 StringDecoder,生产环境通常使用 LengthFieldBasedFrameDecoder(基于长度的帧解码器)来切割数据。

可以试着运行上面的代码,然后用 telnet localhost 8080 / nc localhost 8080 在终端连接它发几条消息试试。

相关推荐
墨雪遗痕2 小时前
工程架构认知(二):从 CDN 到 Keep-Alive,理解流量如何被“消化”在系统之外
java·spring·架构
用户6688599847662 小时前
Sprint Boot登录案例
java
Ivanqhz2 小时前
LLVM IR 转 SMT公式
java·开发语言
一个心烑2 小时前
奖项届定获取方式
java
小红的布丁2 小时前
Reactor 模型详解:单 Reactor、主从 Reactor 与 Netty 思想
android·java·开发语言
weixin_704266053 小时前
redis 的集群
java·数据库·redis
被摘下的星星3 小时前
Java的类加载
java·开发语言
真上帝的左手3 小时前
8. 测试-性能测试-JMeter实战
java·压力测试
cheems95273 小时前
[SpringMVC] SpringWebMVC常见注解介绍
java·springmvc·注解