Netty是什么东西
Netty是一个封装很好的异步事件驱动框架,让我们快速的部署服务端和客户端的网络应用,进行异步IO通信。
1、什么是IO通信
IO就是input 和 output,是一种在两台主机、两个进程或者两个线程之间传输数据的方法
2、什么是异步
异步和同步相对应,同步 举个例子就是:a线程和b线程通信时,a每次准备要读取b的数据时,得在原地等b将数据传过来,不能进行其他操作(b等a也一样)
异步 就是:线程a每次只需要看看线程b有没有传数据过来,有就读取,没有就执行其他操作。
感觉就类似于:a和b两个人要约会,同步情况下,先到约会地点的人得一直在原地等待还没到的人
异步情况下,先到约会地点的人可以一边跟路边的美女(帅哥)搭讪,一边看看对方到了没
所以在同步情况下,服务端的一个线程只能管理一个连接,
在异步情况下,服务端的一个线程可以管理多个连接
Netty怎么使用
Netty怎么用取决于我们要用它做什么
数据传输
数据传输我们要进行IO通信是为了要传输数据
Netty将数据传输的方法封装到了ChannelInboundHandlerAdapter中,继承这个类然后实现里面的方法就可以进行数据传输(应该有点适配器模式)
下面代码中的两个类就是进行数据传输的Handler,其中服务端在连接建立时发送时间,客户端收到数据后打印(来自官方文档)
承载我们要传输的信息、进行传输的数据类型是ByteBuf类型,ByteBuf是一种引用计数的对象,所以我们使用完后,需要注意释放对象。
1 import io.netty.buffer.ByteBuf; 2 import io.netty.channel.ChannelFuture; 3 import io.netty.channel.ChannelFutureListener; 4 import io.netty.channel.ChannelHandlerContext; 5 import io.netty.channel.ChannelInboundHandlerAdapter; 6 7 public class TimeServerHandler extends ChannelInboundHandlerAdapter { 8 9 //在连接建立之后调用该方法 10 @Override 11 public void channelActive(ChannelHandlerContext ctx) throws Exception { 12 final ByteBuf time = ctx.alloc().buffer(4); // (2) 13 time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L)); 14 15 final ChannelFuture f = ctx.writeAndFlush(time); // (3) 16 f.addListener(new ChannelFutureListener() { 17 @Override 18 public void operationComplete(ChannelFuture future) { 19 if (f == future) { 20 ctx.close(); 21 } 22 } 23 }); 24 } 25 //捕捉数据传输过程中的异常 26 @Override 27 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 28 cause.printStackTrace(); 29 ctx.close(); 30 } 31 }
1 import io.netty.buffer.ByteBuf; 2 import io.netty.channel.ChannelHandlerContext; 3 import io.netty.channel.ChannelInboundHandlerAdapter; 4 5 import java.util.Date; 6 7 public class TimeClientHandler extends ChannelInboundHandlerAdapter { 8 9 //收到发送过来的数据之后调用该方法 10 @Override 11 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { 12 13 ByteBuf m = (ByteBuf) msg; // (1) 14 try { 15 long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L; 16 System.out.println(new Date(currentTimeMillis)); 17 ctx.close(); 18 } finally { 19 m.release(); 20 } 21 } 22 23 @Override 24 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 25 26 cause.printStackTrace(); 27 ctx.close(); 28 } 29 }
我们的目的,数据传输的操作已经有了,但现在只是知道了要传输什么,然后还得建立连接来进行传输
建立连接
我们需要使用线程来建立连接(可能协程也可以)
我们不用自己创建线程池,因为Netty提供了线程组(EventLoopGroup类)的方式来建立线程
服务端一般会使用两个线程组,一个用来接收连接,一个用来传输数据;客户端使用一个线程组用来传输数据
现在需要有一个东西把线程组和传输数据的Handler组合起来,为了方便就使用了Bootstrap类,
服务端是ServerBootstrap,客户端是Bootstrap,因为服务端有两个线程组,所以得进行区分
下面是代码中建立连接的类,加上面的Handler就是Netty连接完整的(客户端连接服务端获取时间的)demo
1 import io.netty.bootstrap.ServerBootstrap; 2 import io.netty.channel.*; 3 import io.netty.channel.nio.NioEventLoopGroup; 4 import io.netty.channel.socket.SocketChannel; 5 import io.netty.channel.socket.nio.NioServerSocketChannel; 6 7 public class TimeServer { 8 9 private int port; 10 11 public TimeServer(int port){ 12 this.port = port; 13 } 14 15 public void run(){ 16 17 //称为父线程组 18 EventLoopGroup bossGroup = new NioEventLoopGroup(); 19 //子线程组 20 EventLoopGroup workerGroup = new NioEventLoopGroup(); 21 22 try{ 23 //也可以使用channel直接创建连接 24 ServerBootstrap serverBoostrap = new ServerBootstrap(); 25 serverBoostrap.group(bossGroup,workerGroup) 26 //通道类型有NioServerSocketChannel、OioServerSocketChannel、NioSctpServerChannel(linux平台) 27 //设置服务端通道实现类型 28 .channel(NioServerSocketChannel.class) 29 .childHandler(new ChannelInitializer<SocketChannel>() { 30 @Override 31 protected void initChannel(SocketChannel socketChannel) throws Exception { 32 33 // ChannelPipeline用于管理Handler 34 socketChannel.pipeline().addLast(new TimeServerHandler()); 35 } 36 }) 37 //设置线程队列的连接个数 38 .option(ChannelOption.SO_BACKLOG,128) 39 //对子线程组的配置,设置保持活动连接状态,默认为false,会主动探测空闲连接的有效性 40 .childOption(ChannelOption.SO_KEEPALIVE,true); 41 42 //ChannelFuture是指尚未执行的操作,因为netty是异步操作 43 ChannelFuture channelFuture = serverBoostrap.bind(port).sync(); 44 //可以得到channle的各种状态 45 channelFuture.channel().isOpen(); 46 channelFuture.channel().isActive(); 47 48 //直到服务器socket关闭的时候执行。 49 channelFuture.channel().closeFuture().sync(); 50 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } finally { 54 //关闭线程组 55 bossGroup.shutdownGracefully(); 56 workerGroup.shutdownGracefully(); 57 } 58 59 } 60 61 public static void main(String[] args) { 62 TimeServer timeServer = new TimeServer(8989); 63 timeServer.run(); 64 } 65 66 67 }
1 import io.netty.bootstrap.Bootstrap; 2 import io.netty.channel.ChannelFuture; 3 import io.netty.channel.ChannelInitializer; 4 import io.netty.channel.ChannelOption; 5 import io.netty.channel.EventLoopGroup; 6 import io.netty.channel.nio.NioEventLoopGroup; 7 import io.netty.channel.socket.SocketChannel; 8 import io.netty.channel.socket.nio.NioSocketChannel; 9 10 public class TimeClient { 11 12 public static void main(String[] args) { 13 String host = "127.0.0.1"; 14 int port = Integer.parseInt("8989"); 15 EventLoopGroup workerGroup = new NioEventLoopGroup(); 16 17 try { 18 Bootstrap b = new Bootstrap(); 19 b.group(workerGroup); 20 b.channel(NioSocketChannel.class); 21 b.option(ChannelOption.SO_KEEPALIVE, true); 22 b.handler(new ChannelInitializer<SocketChannel>() { 23 @Override 24 public void initChannel(SocketChannel ch) throws Exception { 25 ch.pipeline().addLast(new TimeClientHandler()); 26 } 27 }); 28 29 30 ChannelFuture f = b.connect(host, port).sync(); 31 32 33 f.channel().closeFuture().sync(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } finally { 37 workerGroup.shutdownGracefully(); 38 } 39 } 40 }