小白也能看懂的 Netty 框架——实战(一)
一、架构回顾
简易Netty框架

进一步查看BossGroup

Netty的详细结构

Netty抽象出了两组线程池:
- BossGroup:专门负责接受客户端的连接。
- WorkerGroup:负责网络的读写。
这两者都是
NIOEventLoopGroup类型。
NIOEventLoopGroup 相当于一个事件循环组,它内部包含多个事件,每个事件都是 NIOEventLoop。
NIOEventLoop 是一个持续循环执行处理任务的线程,每个线程都有一个 selector,用于监听绑定在上面的 Socket 网络通信。
NIOEventLoopGroup 可以有多个线程,也可以包含多个 NIOEventLoop。
每个 Boss NIOEventLoop 的执行步骤:
- 轮询
accept事件。 - 处理
accept事件,建立与客户端的连接,生成NIOSocketChannel,并将其注册到某个 Worker 的NIOEventLoop上的selector。 - 处理任务队列中的任务,即执行
runAllTasks。
每个 Worker NIOEventLoop 的执行步骤:
- 轮询
read和write事件。 - 处理 I/O 事件,即处理
read和write操作,更新对应的NIOSocketChannel。 - 处理任务队列中的任务,即执行
runAllTasks。
在处理业务时,每个 Worker NIOEventLoop 会使用 pipeline,其中包含一条管道。通过 pipeline,可以获取到处理器、拦截器等组件,从而实现定制化的业务处理。
这是对代码的分析及说明:
二、代码实战
服务端
nettyServer.java
package netty.simple;
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.channel.socket.nio.NioSocketChannel;
public class nettyServer {
public static void main(String[] args) throws InterruptedException {
// 创建两个group,分别用于boss和worker
EventLoopGroup bossGroup = new NioEventLoopGroup(); // 处理连接请求
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 处理I/O操作
try {
ServerBootstrap bootstrap = new ServerBootstrap(); // 创建服务端启动对象
bootstrap.group(bossGroup, workerGroup) // 设置两个线程组
.channel(NioServerSocketChannel.class) // 使用 NioServerSocketChannel 作为通道实现
.option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待的连接个数
.childOption(ChannelOption.SO_KEEPALIVE, true) // 保持活动连接状态
.childHandler(new ChannelInitializer<SocketChannel>() { // 创建通道初始化器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyServerHandler()); // 添加自定义处理器
}
});
System.out.println("服务器已准备好");
// 绑定端口并启动服务器
ChannelFuture cf = bootstrap.bind(6668).sync(); // 绑定端口并同步等待
// 监听关闭事件
cf.channel().closeFuture().sync(); // 阻塞直到关闭
} finally {
bossGroup.shutdownGracefully(); // 优雅关闭
workerGroup.shutdownGracefully(); // 优雅关闭
}
}
}
说明:
- 该服务端代码创建了两个事件循环组:
bossGroup用于接受客户端的连接请求,workerGroup用于处理业务请求。 ServerBootstrap是 Netty 服务端的启动引导类,配置了通道、选项、以及事件处理器。ChannelInitializer<SocketChannel>用于初始化每个客户端连接时创建的SocketChannel,并将自定义的NettyServerHandler加入到通道的处理链中。- 最后,服务端绑定端口
6668,并通过closeFuture().sync()监听关闭事件。
NettyServerHandler.java
package netty.simple;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server ctx:" + ctx);
// 将 msg 转为 ByteBuf
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("客户端发送消息:" + byteBuf.toString(CharsetUtil.UTF_8));
System.out.println("客户端地址:" + ctx.channel().remoteAddress());
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// 数据写入并刷新
ctx.writeAndFlush(Unpooled.copiedBuffer("hello client : miaowu", CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.channel().close(); // 发生异常时关闭通道
}
}
说明:
channelRead方法读取客户端发送的消息,并打印消息内容和客户端地址。channelReadComplete在读取完数据后写入数据并刷新,回应客户端的消息。exceptionCaught处理异常,并在发生错误时关闭通道。
客户端
nettyClient.java
package netty.simple;
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;
public class nettyClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup eventExecutors = new NioEventLoopGroup(); // 创建事件循环组
try {
Bootstrap bootstrap = new Bootstrap(); // 创建客户端启动对象
bootstrap.group(eventExecutors) // 设置线程组
.channel(NioSocketChannel.class) // 使用 NioSocketChannel 作为通道实现
.handler(new ChannelInitializer<SocketChannel>() { // 配置通道处理器
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NettyClientHandler()); // 添加自定义客户端处理器
}
});
System.out.println("客户端启动成功");
// 启动客户端并连接到服务端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync(); // 连接到服务端
channelFuture.channel().closeFuture().sync(); // 阻塞直到连接关闭
} finally {
eventExecutors.shutdownGracefully(); // 优雅关闭
}
}
}
说明:
EventLoopGroup用于管理线程。Bootstrap是客户端的启动引导类,配置了线程组、通道实现以及通道处理器。- 客户端通过
connect()方法连接到服务端,使用closeFuture().sync()阻塞直到关闭连接。
NettyClientHandler.java
package netty.simple;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client " + ctx);
ctx.writeAndFlush(Unpooled.copiedBuffer("hello server : miaowu", CharsetUtil.UTF_8)); // 向服务器发送数据
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
System.out.println("服务器回复的消息:" + byteBuf.toString(CharsetUtil.UTF_8)); // 输出服务器返回的数据
System.out.println("服务器地址:" + ctx.channel().remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close(); // 发生异常时关闭通道
}
}
说明:
channelActive方法在连接建立后自动触发,客户端向服务端发送数据。channelRead方法用于读取服务端返回的数据,并打印消息内容和服务器地址。exceptionCaught处理异常,并在发生错误时关闭通道。
总结
- 服务端:使用
ServerBootstrap创建服务端,配置了NioEventLoopGroup,分别负责接收连接和处理业务。 - 客户端:使用
Bootstrap创建客户端,配置了NioSocketChannel并与服务端建立连接。 - Netty的异步模型:客户端和服务端之间的通信是异步的,通过
ChannelFuture进行异步操作,确保不会阻塞主线程。
&spm=1001.2101.3001.5002&articleId=144242590&d=1&t=3&u=4dc76fd5135b46f294c86ec60392fefa)
1597

被折叠的 条评论
为什么被折叠?



