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 }