Netty的启动过程涉及多个关键组件,其中ServerBootstrap是入口。在启动流程中,我们通过以下步骤逐一深入了解:
1.创建 EventLoopGroup:
在启动过程中,首先需要创建两个 EventLoopGroup 实例,分别用于处理连接(bossGroup)和处理业务逻辑(workerGroup)。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
这两个 EventLoopGroup 使用 NIO 进行事件处理,NioEventLoopGroup 内部会创建一定数量的 NioEventLoop 实例,每个实例运行在一个单独的线程上。
2 配置 ServerBootstrap
创建 ServerBootstrap 实例,将 bossGroup 和 workerGroup 配置到其中,并设置服务器的通信协议和处理器。
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new YourChannelInitializer());
// ... 其他配置
在这一步,通过 channel 方法指定使用 NIO 传输,childHandler 方法设置一个 ChannelInitializer,用于初始化新连接的 ChannelPipeline。
3 绑定端口并启动服务器
调用 bind 方法绑定端口,并通过 sync 方法等待绑定完成。在端口绑定完成后,通过 closeFuture 方法等待服务器关闭。
serverBootstrap.bind(8080).sync().channel().closeFuture().sync();
这一步启动了整个 Netty 服务器,服务器开始监听端口,等待客户端连接。
在 Netty 中,Channel 和 EventLoop 是关键的组件。它们的初始化过程在 ChannelInitializer 中进行。让我们深入了解这两个组件的初始化过程。
1 Channel 初始化
Channel 的初始化过程主要包括创建、注册、活跃和关闭等阶段。在 ChannelInitializer 的 initChannel 方法中,我们可以通过 ChannelPipeline 对 Channel 进行初始化。
public class YourChannelInitializer extends ChannelInitializer<SocketChannel> {
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
// 添加业务逻辑处理器
pipeline.addLast(new YourBusinessLogicHandler());
}
}
在这个示例中,我们通过 ChannelPipeline 添加了一个业务逻辑处理器,该处理器会处理从客户端接收到的数据。
2 EventLoop 初始化
EventLoop 在初始化时会创建任务队列,并启动一个线程用于执行任务。NioEventLoop 的初始化过程涉及到创建 Selector 和绑定到当前线程。具体的初始化过程在 NioEventLoop 的构造函数中,涉及到线程的创建、SelectorProvider 的选择以及任务队列的初始化。
public NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider) {
// ... 其他初始化
this.selector = openSelector();
// ... 其他初始化
}
编写一个完整的 Netty 客户端和服务端示例涉及多个步骤,包括创建 ServerBootstrap、配置 EventLoopGroup、实现 ChannelInitializer 等。以下是一个简单的示例,覆盖了这些基本步骤,并尽可能深入地解释了每一步。
服务端代码:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) {
// 创建两个 EventLoopGroup,bossGroup 用于接收连接,workerGroup 用于处理连接上的业务逻辑
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建 ServerBootstrap 对象,用于配置服务器
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 指定使用 NIO 传输
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new NettyServerHandler()); // 添加业务逻辑处理器
}
})
.option(ChannelOption.SO_BACKLOG, 128) // 设置队列大小
.childOption(ChannelOption.SO_KEEPALIVE, true); // 保持连接
// 绑定端口并启动服务器
serverBootstrap.bind(8080).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 优雅地关闭 EventLoopGroup
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务端业务逻辑处理器 NettyServerHandler:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 读取客户端发送的数据
ByteBuf byteBuf = (ByteBuf) msg;
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
System.out.println("Server received: " + new String(data));
// 向客户端发送数据
ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("Hello, Client!".getBytes()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 异常处理
cause.printStackTrace();
ctx.close();
}
}
客户端代码:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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) {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建 Bootstrap 对象,用于配置客户端
Bootstrap clientBootstrap = new Bootstrap();
clientBootstrap.group(workerGroup)
.channel(NioSocketChannel.class) // 指定使用 NIO 传输
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new NettyClientHandler()); // 添加业务逻辑处理器
}
})
.option(ChannelOption.SO_KEEPALIVE, true); // 保持连接
// 连接到服务器
clientBootstrap.connect("localhost", 8080).sync().channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 优雅地关闭 EventLoopGroup
workerGroup.shutdownGracefully();
}
}
}
客户端业务逻辑处理器 NettyClientHandler:
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
// 客户端连接激活时发送数据
ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("Hello, Server!".getBytes()));
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 读取服务端发送的数据
ByteBuf byteBuf = (ByteBuf) msg;
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
System.out.println("Client received: " + new String(data));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 异常处理
cause.printStackTrace();
ctx.close();
}
}
运行过程:
1.启动服务端: 运行 NettyServer 类,服务端开始监听端口 8080。
2.启动客户端: 运行 NettyClient 类,客户端连接到服务端。
3.数据交互: 客户端连接成功后,会触发 channelActive 方法,向服务端发送数据。服务端收到数据后,会触发 channelRead 方法,处理数据并向客户端发送响应。客户端收到响应后,会触发 channelRead 方法,处理服务端的响应数据。
4.关闭连接: 当数据交互完成后,客户端和服务端都可以通过 ctx.close() 主动关闭连接。在示例中,客户端和服务端分别在异常处理中关闭连接。
这个简单的示例演示了 Netty 客户端和服务端的基本交互过程。在实际应用中,你可能需要根据具体业务需求添加更多的处理器、编解码器等组件,以满足特定的需求。