《尚硅谷 - Netty 核心技术及源码剖析教程:从 IO 模型到高性能通信,吃透底层原理》不仅深入介绍了Netty的核心原理,还通过大量的示例代码帮助读者理解底层实现和实际应用。在这里,我们将通过一个简单的代码示例,演示Netty框架如何构建一个高效的网络通信应用。
1. Netty的基础代码示例
在本例中,我们将创建一个简单的Echo服务器和客户端。服务器接收到客户端的消息后,会原样返回。
1.1 EchoServer(服务器端代码)
java
package com.example.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
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.NioServerSocketChannel;
public class EchoServer {
private final int port;
public EchoServer(int port) {
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // 负责接收客户端连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); // 负责处理I/O操作
try {
ServerBootstrap b = new ServerBootstrap(); // 启动引导类
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 使用NIO传输类型
.childHandler(new ChannelInitializer<SocketChannel>() { // 初始化每个接入的客户端Channel
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoServerHandler()); // 添加处理逻辑
}
});
ChannelFuture f = b.bind(port).sync(); // 绑定端口,开始接收进来的连接
f.channel().closeFuture().sync(); // 等待服务器关闭
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoServer(8080).start(); // 启动服务器,监听8080端口
}
}
1.2 EchoServerHandler(服务器端的Handler)
java
package com.example.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 将接收到的消息写回客户端
ByteBuf in = (ByteBuf) msg;
System.out.println("Server received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
ctx.write(msg); // 发送到客户端
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush(); // 刷新所有写操作
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close(); // 关闭连接
}
}
1.3 EchoClient(客户端代码)
java
package com.example.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
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 EchoClient {
private final String host;
private final int port;
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup(); // 事件循环组
try {
Bootstrap b = new Bootstrap(); // 启动引导类
b.group(group)
.channel(NioSocketChannel.class) // 使用NIO传输类型
.handler(new ChannelInitializer<SocketChannel>() { // 初始化客户端Channel
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler()); // 添加处理逻辑
}
});
ChannelFuture f = b.connect(host, port).sync(); // 连接到服务器
f.channel().closeFuture().sync(); // 等待关闭
} finally {
group.shutdownGracefully(); // 关闭线程池
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("localhost", 8080).start(); // 连接到localhost:8080
}
}
1.4 EchoClientHandler(客户端的Handler)
java
package com.example.netty;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String message = "Hello, Netty!";
ByteBuf encoded = ctx.alloc().buffer(4 * message.length());
encoded.writeBytes(message.getBytes());
ctx.writeAndFlush(encoded); // 向服务器发送消息
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
System.out.println("Client received: " + in.toString(io.netty.util.CharsetUtil.UTF_8));
ctx.close(); // 关闭连接
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close(); // 关闭连接
}
}
2. 代码解析
-
服务器端(EchoServer):
- 使用
ServerBootstrap
来启动服务器,NioServerSocketChannel
是Netty提供的一个实现TCP/IP协议的Channel类型。 ChannelInitializer
初始化每个连接的Channel,并在其Pipeline中添加一个EchoServerHandler
来处理收到的数据。
- 使用
-
客户端(EchoClient):
- 使用
Bootstrap
启动客户端,NioSocketChannel
是客户端连接时的Channel类型。 - 连接到服务器后,客户端发送一个简单的消息
Hello, Netty!
,并在接收到服务器响应后打印。
- 使用
-
数据处理(Handler):
- EchoServerHandler:负责接收客户端的消息并将其原样返回。
- EchoClientHandler:客户端接收到服务器的回应后,打印返回的消息并关闭连接。
3. 运行效果
- 启动
EchoServer
服务器,监听8080端口。 - 启动
EchoClient
客户端,连接到服务器并发送Hello, Netty!
。 - 服务器接收到客户端消息后,原样返回给客户端,客户端打印出
Server received: Hello, Netty!
。
4. 总结
通过这个简单的Echo服务器和客户端示例,展示了Netty框架的基础使用方法。Netty的核心优势在于其高性能的I/O处理、灵活的线程模型和强大的扩展能力。通过深入分析Netty的源码,开发者可以更好地理解底层的I/O模型和优化技术,从而在实际应用中充分发挥Netty的高效能。