目标
这一系列文章会先通过官方网站的一些示例作为起始,后续会穿插一些本人项目中运用到的一些知识点和案例代码进去,帮助大家理解和使用Netty。如果时间充足会考虑用Netty实现一个应用示例。
本系列使用的是Netty4版本
什么是Netty
Netty 是一款高性能、异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。它极大地简化了 TCP 和 UDP 套接字服务器等网络编程的复杂性,使得开发人员可以专注于业务逻辑的实现,而无需过多关注底层网络编程的细节。
为什么使用Netty
使用Netty作为网络编程框架的原因有很多,以下是一些关键的考量点:
-
高性能:在构建高性能的网络应用时,Netty的异步非阻塞I/O模型能够显著提升吞吐量并降低延迟。Netty内部使用Reactor模式,这使得它能够同时处理成千上万的连接,而不会造成资源瓶颈。
-
简单易用:Netty的API设计简洁明了,降低了学习曲线,使开发者能够快速上手。同时,Netty提供了丰富的文档和示例代码,帮助开发者更好地理解框架的工作原理和最佳实践。
-
可扩展性:Netty的模块化设计允许开发者根据需求定制和扩展功能。通过ChannelHandler、Codec和Pipeline等组件的组合,开发者可以轻松实现自定义的协议、编解码逻辑和业务处理流程。
-
丰富的功能:Netty不仅提供了基本的TCP/UDP套接字支持,还提供了HTTP、WebSocket、SSL/TLS等高级功能的实现。这使得开发者无需从头开始编写这些复杂协议的实现代码,从而大大节省了开发时间。
-
稳定性和健壮性:Netty经过广泛的测试和优化,能够在各种网络环境下稳定运行。它提供了丰富的异常处理和容错机制,确保应用在网络波动或异常情况下仍能保持正常工作。
使用Netty构建一个简单的网络应用
这里我们引用官方的一个示例做解释
package io.netty.example.discard;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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;
/**
* Discards any incoming data.
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// 绑定端口等待连接
ChannelFuture f = b.bind(port).sync(); // (7)
// 等待服务关闭,这里会阻塞同步等待
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
上述示例代码中按编号作解释
- NioEventLoopGroup是一个处理I/O操作的多线程事件循环。Netty为不同类型的传输提供了各种EventLoopGroup实现。在本例中,我们正在实现一个服务器端应用程序,因此将使用两个NioEventLoopGroup。第一个,通常被称为 “boss”,接受处理服务器的连接事件。第二个通常被称为“worker”,一旦server接受连接并将接受的连接注册给worker,它就会处理后续连接的IO事件。
- ServerBootstrap是一个帮助类,用于创建Server。对应客户端使用的是Bootstrap。
- 指定handler,用NioServerSocketChannel类实例化以处理连接事件 。
- 指定连接的处理handler。ChannelInitializer是一个特殊的处理程序,用于配置新的handler。我们可以在里面通过Pipeline添加多个handler形成一个数据处理的流程处理我们的业务数据如DiscardServerHandler。
- 设置特定于通道实现的参数,keepAlive之类的套接字选项。
- option和childOption的区别;option用于设置接受传入连接的NioServerSocketChannel,及连接事件的处理。childOption用于设置父ServerChannel已接受连接的通道。
- 绑定服务端口等待连接
下面看示例中的handler代码
package io.netty.example.discard;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerAdapter;
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
((ByteBuf) msg).release(); // (3)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
cause.printStackTrace();
ctx.close();
}
}
- DiscardServerHandler扩展了ChannelInboundHandlerAdapter ,它是ChannelInboundHandler的一个实现,用于处理接收到的数据。
- 重写channelRead()事件处理程序方法。每当从客户端接收到新数据时,就会调用此方法并传入收到的消息。在本例中,接收到的消息的类型为ByteBuf(和Java Nio中的ByteBuffer不是一个东西, Netty做了更好的封装)。
- 实现DISCARD协议,其实就是忽略接收到的消息,不回复。ByteBuf是一个引用计数的对象,必须通过release()方法显式释放。
- 如果handler中产生异常会回调这里,通过ctx.close关闭连接
通过上面的示例代码可以用Netty简单实现一个网络程序