前言
上一节,我们简单的介绍了Java 里面NIO的基本用法。我们都知道,我们使用Netty大部分情况都是为了使用它的NIO的。那么这一节,我们也基于Netty来写一个和上一节的一样的功能做下对比吧。
1. 服务端
-
NioServer
public class NioServer {
public static void main(String[] args) {
int port = 8080;
ServerBootstrap serverBootstrap = new ServerBootstrap();
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NioServerHandler());
}
});
ChannelFuture bind = serverBootstrap.bind(port);
try {
bind.channel().closeFuture().sync();
} catch (InterruptedException e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
上面是Netty服务端的入口代码,这里相比Java的服务端,代码就要稍微多了一点了,但是总体可以分为三部分:
-
创建
ServerBootstrap
,EventLoopGroup
-
配置相关的配置
-
绑定端口,并在异常时优雅关闭
-
NioServerHandler
public class NioServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "utf-8"); System.out.println("server received :" + body); ByteBuf resp = Unpooled.copiedBuffer(body.getBytes(StandardCharsets.UTF_8)); ctx.write(resp); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } }
上面是处理服务端发送消息相关的代码。和Java的处理逻辑比起来,这里的代码逻辑简直是少的吓人。基本上就实现了两个方法,就完成了消息的接收和发送的逻辑。乖乖,光是剩下的代码量得扣掉多少绩效(抖个机灵)。
2. 客户端
-
NioClient
public class NioClient {
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
EventLoopGroup group = new NioEventLoopGroup();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new NioClientHandler());
}
}
);
ChannelFuture channelFuture = bootstrap.connect("localhost", 8080);
try {
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
group.shutdownGracefully();
}
}
}
上面是Netty客户端的代码。可以看到除了Bootstrap用的不一样,总的来说也是和服务端代码没啥区别的。
-
NioClientHandler
public class NioClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { Scanner scanner = new Scanner(System.in); System.out.println("请输入要发送的消息"); String message = scanner.next(); byte[] req = message.getBytes(StandardCharsets.UTF_8); ByteBuf buf = Unpooled.buffer(req.length); buf.writeBytes(req); ctx.writeAndFlush(buf); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; byte[] req = new byte[buf.readableBytes()]; buf.readBytes(req); String body = new String(req, "utf-8"); System.out.println("client received :" + body); } }
这里也是一样的,和Java的客户端繁杂的处理SelectionKey的逻辑比起来,这里的代码简直可以说是少的可怜。
这一节我们只是简单的介绍Netty的基本使用方法,下一节,我们将从源码分析,介绍Netty是如何把Java的NIO的代码封装成如此好用的。