netty实战
Netty是一款异步的事件驱动的网络应用程序框架,支持快速开发可维护的高性能的面向协议的服务器和客户端。
1、pom依赖
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.32.Final</version> </dependency>
2、实战:客户端与服务端双向通信
客户端/服务端连接读写逻辑处理均是启动阶段通过给逻辑处理链 Pipeline 添加逻辑处理器实现连接数据的读写逻辑.
写数据调用writeAndFlush()方法,客户端与服务端交互的二进制数据传输载体为ByteBuf,ByteBuf通过连接的内存管理器创建即ctx.alloc().buffer(),通过writeBytes()方法将字节数据填充到ByteBuf 写到对端.
@Data public class EchoServer { private final int port; public EchoServer(int port) { this.port = port; } public static void main(String[] args) throws InterruptedException { new EchoServer(8089).start(); } public void start() throws InterruptedException { final EchoServerHandler echoServerHandler = new EchoServerHandler(); //创建EventLoopGroup,处理事件 EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker) //指定所使用的NIO传输 Channel .channel(NioServerSocketChannel.class) //使用指定的端口设置套接字地址 .localAddress(new InetSocketAddress(port)) //添加一个EchoServerHandler到子Channel的ChannelPipeline .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(echoServerHandler); } }); //异步的绑定服务器,调用sync()方法阻塞等待直到绑定完成 ChannelFuture channelFuture = bootstrap.bind().sync(); // 等待服务端监听端口关闭 channelFuture.channel().closeFuture().sync(); } finally { //关闭EventLoopGroup,释放所有的资源 boss.shutdownGracefully().sync(); worker.shutdownGracefully().sync(); } } }
服务端创建流程:

- 创建 ServerBootStrap实例
- 设置并绑定
NioEventLoopGroup线程池 - 通过
ServerBootStrap的channel方法设置并绑定服务端 Channel - 创建并初始化
ChannelPipeline - 添加并设置
ChannelHandler - 绑定并启动监听端口
Channel是 Netty中的网络操作抽象类,对应JDK底层的Socket,它除了包含基本的I/O操作,如 bind(),connect(),read(),write()之外,还包括了Netty框架相关的一些功能,如获取 Channel的EventLoop。
EventLoop定义了Netty的核心抽象,用于处理连接的生命周期中所发生的事件。EventLoop 为Channel处理I/O操作
一个EventLoopGroup 包含一个或者多个EventLoop 一个 EventLoop 在它的生命周期内只和一个Thread绑定 所有由 EventLoop处理的 I/O事件都将在它专有的Thread上被处理 一个 Channel 在它的生命周期内只注册一个EventLoop 一个 EventLoop 可能会被分配给一个或多个 Channel
EventLoopGroup实际上就是处理I/O操作的线程池,负责为每个新注册的Channel分配一个EventLoop,Channel在整个生命周期都有其绑定的 EventLoop来服务。
NioEventLoop 就是 EventLoop的一个重要实现类,NioEventLoop 是Netty内部的I/O线程,而 NioEventLoopGroup是拥有 NioEventLoop的线程池,在Netty服务端中一般存在两个这样的NioEventLoopGroup线程池,一个 “Boss” 线程池,用于接收客户端连接,实际上该线程池中只有一个线程,一个 “Worker”线程池用于处理每个连接的读写。而Netty客户端只需一个线程池即可,主要用于处理连接中的读写操作。
ChannelHandler是Netty的主要组件,主要用于对出站和入站数据进行处理,有两个重要的子接口:
- ChannelInboundHandler——处理入站数据以及各种状态变化
- ChannelOutboundHandler——处理出站数据并且允许拦截所有的操作
ChannelPipeline提供 ChannelHandler链的容器,换句话说,就是一个逻辑处理链,用于拦截流经Channel的入站和出站事件的ChannelHandler。就是当 Channel被创建时,会被自动地分配到它的专属的 ChannelPipeline。
当一个消息或者任何其他的入站事件被读取时,会从 ChannelPipeline的头部开始流动,并被传递给第一个 ChannelInboundHandler,第一个处理完成之后传递给下一个 ChannelInboundHandler,一直到ChannelPipeline的尾端,与之对应的是,当数据被写出时,数据从 ChannelOutboundHandler 链的尾端开始流动,直到它到达链的头部为止。
Netty中所有I/O操作都是异步的,使用ChannelFuture可以获取操作完成的结果,其 addListener()方法注册了一个 ChannelFutureListener,以便在某个操作完成时(无论是否成功)得到通知。
Netty提供的启动辅助类,帮助Netty客户端或服务端的Netty初始化,服务端对应的是 ServerBootStrap引导类。
package com.smart.netty.server; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.util.CharsetUtil; //标识一个 ChannelHandler可以被多个Channel安全地共享 @ChannelHandler.Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf byteBuf = (ByteBuf) msg; System.out.println("接收到消息:" + byteBuf.toString(CharsetUtil.UTF_8)); //将接受到消息回写给发送者 ctx.write(byteBuf); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.EMPTY_BUFFER) .addListener(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
客户端

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 loopGroup = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(loopGroup) .channel(NioSocketChannel.class) .remoteAddress(new InetSocketAddress(host, port)) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new EchoClientHandler()); } }); ChannelFuture channelFuture = bootstrap.connect().sync(); channelFuture.channel().closeFuture().sync(); } finally { loopGroup.shutdownGracefully().sync(); } } public static void main(String[] args) throws InterruptedException { new EchoClient("127.0.0.1", 8089).start(); } }
@ChannelHandler.Sharable public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> { @Override protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception { System.out.println("客户端接收消息:" + msg.toString(CharsetUtil.UTF_8)); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8)); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
浙公网安备 33010602011771号