译 3个netty5的例子,简单介绍netty的用法


   这是一个netty快速入门的例子,也是我的学习笔记,比较简单,翻译于官方的文档整理后把所有代码注释放在每一行代码中间,简单明了地介绍一些基础的用法。

   首页这是基于netty5的例子,如果需要使用请依赖netty5的包。maven引用方式

1 <dependency>
2     <groupId>io.netty</groupId>
3     <artifactId>netty-all</artifactId>
4     <version>5.0.0.Alpha2</version>
5 </dependency>

或者去下载最新的jar下载页面

1.DISCARD服务(丢弃服务,指的是会忽略所有接收的数据的一种协议)

001 import io.netty.bootstrap.ServerBootstrap;
002 import io.netty.channel.ChannelFuture;
003 import io.netty.channel.ChannelInitializer;
004 import io.netty.channel.ChannelOption;
005 import io.netty.channel.EventLoopGroup;
006 import io.netty.channel.nio.NioEventLoopGroup;
007 import io.netty.channel.socket.SocketChannel;
008 import io.netty.channel.socket.nio.NioServerSocketChannel;
009  
010 /**
011  * 处理数据
012  */
013 public class NettyServer {
014     private int port;
015     public NettyServer(int port) {
016         this.port = port;
017     }
018     public void run() throws Exception {
019         /***
020          * NioEventLoopGroup 是用来处理I/O操作的多线程事件循环器,
021          * Netty提供了许多不同的EventLoopGroup的实现用来处理不同传输协议。
022          * 在这个例子中我们实现了一个服务端的应用,
023          * 因此会有2个NioEventLoopGroup会被使用。
024          * 第一个经常被叫做‘boss’,用来接收进来的连接。
025          * 第二个经常被叫做‘worker’,用来处理已经被接收的连接,
026          * 一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。
027          * 如何知道多少个线程已经被使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,
028          * 并且可以通过构造函数来配置他们的关系。
029          */
030         EventLoopGroup bossGroup = new NioEventLoopGroup();
031         EventLoopGroup workerGroup = new NioEventLoopGroup();
032         System.out.println("准备运行端口:" + port);
033         try {
034             /**
035              * ServerBootstrap 是一个启动NIO服务的辅助启动类
036              * 你可以在这个服务中直接使用Channel
037              */
038             ServerBootstrap b = new ServerBootstrap();
039             /**
040              * 这一步是必须的,如果没有设置group将会报java.lang.IllegalStateException: group not set异常
041              */
042             b = b.group(bossGroup, workerGroup);
043             /***
044              * ServerSocketChannel以NIO的selector为基础进行实现的,用来接收新的连接
045              * 这里告诉Channel如何获取新的连接.
046              */
047             b = b.channel(NioServerSocketChannel.class);
048             /***
049              * 这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。
050              * ChannelInitializer是一个特殊的处理类,
051              * 他的目的是帮助使用者配置一个新的Channel。
052              * 也许你想通过增加一些处理类比如NettyServerHandler来配置一个新的Channel
053              * 或者其对应的ChannelPipeline来实现你的网络程序。
054              * 当你的程序变的复杂时,可能你会增加更多的处理类到pipline上,
055              * 然后提取这些匿名类到最顶层的类上。
056              */
057             b = b.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
058                 @Override
059                 public void initChannel(SocketChannel ch) throws Exception {
060                    ch.pipeline().addLast(new DiscardServerHandler());
061                    //ch.pipeline().addLast(new ResponseServerHandler());
062                    // ch.pipeline().addLast(new TimeServerHandler());
063                 }
064             });
065             /***
066              * 你可以设置这里指定的通道实现的配置参数。
067              * 我们正在写一个TCP/IP的服务端,
068              * 因此我们被允许设置socket的参数选项比如tcpNoDelay和keepAlive。
069              * 请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识。
070              */
071             b = b.option(ChannelOption.SO_BACKLOG, 128);
072             /***
073              * option()是提供给NioServerSocketChannel用来接收进来的连接。
074              * childOption()是提供给由父管道ServerChannel接收到的连接,
075              * 在这个例子中也是NioServerSocketChannel。
076              */
077             b = b.childOption(ChannelOption.SO_KEEPALIVE, true);
078             /***
079              * 绑定端口并启动去接收进来的连接
080              */
081             ChannelFuture f = b.bind(port).sync();
082             /**
083              * 这里会一直等待,直到socket被关闭
084              */
085             f.channel().closeFuture().sync();
086         finally {
087             /***
088              * 优雅关闭
089              */
090             workerGroup.shutdownGracefully();
091             bossGroup.shutdownGracefully();
092         }
093     }
094  
095     public static void main(String[] args) throws Exception {
096         int port;
097         if (args.length > 0) {
098             port = Integer.parseInt(args[0]);
099         else {
100             port = 8000;
101         }
102         new NettyServer(port).run();
103     }
104 }
01 import io.netty.buffer.ByteBuf;
02 import io.netty.channel.ChannelHandlerAdapter;
03 import io.netty.channel.ChannelHandlerContext;
04 import io.netty.util.CharsetUtil;
05 import io.netty.util.ReferenceCountUtil;
06  
07 /**
08  * 服务端处理通道.这里只是打印一下请求的内容,并不对请求进行任何的响应
09  * DiscardServerHandler 继承自 ChannelHandlerAdapter,
10  * 这个类实现了ChannelHandler接口,
11  * ChannelHandler提供了许多事件处理的接口方法,
12  * 然后你可以覆盖这些方法。
13  * 现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。
14  *
15  */
16 public class DiscardServerHandler extends ChannelHandlerAdapter {
17  
18     /***
19      * 这里我们覆盖了chanelRead()事件处理方法。
20      * 每当从客户端收到新的数据时,
21      * 这个方法会在收到消息时被调用,
22      * 这个例子中,收到的消息的类型是ByteBuf
23      * @param ctx 通道处理的上下文信息
24      * @param msg 接收的消息
25      */
26     @Override
27     public void channelRead(ChannelHandlerContext ctx, Object msg) {
28         try {
29             ByteBuf in = (ByteBuf) msg;
30           /*  while (in.isReadable()) {
31                 System.out.print((char) in.readByte());
32                 System.out.flush();
33             }*/
34             //这一句和上面注释的的效果都是打印输入的字符
35             System.out.println(in.toString(CharsetUtil.US_ASCII));
36         }finally {
37             /**
38              * ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。
39              * 请记住处理器的职责是释放所有传递到处理器的引用计数对象。
40              */
41             ReferenceCountUtil.release(msg);
42         }
43     }
44  
45     /***
46      * 这个方法会在发生异常时触发
47      * @param ctx
48      * @param cause
49      */
50     @Override
51     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
52         /***
53          * 发生异常后,关闭连接
54          */
55         cause.printStackTrace();
56         ctx.close();
57     }
58  
59 }

以上是一个丢弃服务的处理方式,你可以运行后通过telnet来发送消息,来查看是否正常运行,注意console里会打印你的输入内容。

2.ECHO服务(响应式协议)

    到目前为止,我们虽然接收到了数据,但没有做任何的响应。然而一个服务端通常会对一个请求作出响应。让我们学习怎样在ECHO协议的实现下编写一个响应消息给客户端,这个协议针对任何接收的数据都会返回一个响应。

    和discard server唯一不同的是把在此之前我们实现的channelRead()方法,返回所有的数据替代打印接收数据到控制台上的逻辑。

说明NettyServer 还是用上面已经提供的类,只是把这段里的注销部分修改成如下。


1 //ch.pipeline().addLast(new DiscardServerHandler());        
2 ch.pipeline().addLast(new ResponseServerHandler());
3 //ch.pipeline().addLast(new TimeServerHandler());

下面是处理类ResponseServerHandler的代码

01 import io.netty.channel.ChannelHandlerAdapter;
02 import io.netty.channel.ChannelHandlerContext;
03  
04 /**
05  * 服务端处理通道.
06  * ResponseServerHandler 继承自 ChannelHandlerAdapter,
07  * 这个类实现了ChannelHandler接口,
08  * ChannelHandler提供了许多事件处理的接口方法,
09  * 然后你可以覆盖这些方法。
10  * 现在仅仅只需要继承ChannelHandlerAdapter类而不是你自己去实现接口方法。
11  * 用来对请求响应
12  */
13 public class ResponseServerHandler extends ChannelHandlerAdapter {
14  
15     /**
16      * 这里我们覆盖了chanelRead()事件处理方法。
17      * 每当从客户端收到新的数据时,
18      * 这个方法会在收到消息时被调用,
19      *ChannelHandlerContext对象提供了许多操作,
20      * 使你能够触发各种各样的I/O事件和操作。
21      * 这里我们调用了write(Object)方法来逐字地把接受到的消息写入
22      * @param ctx 通道处理的上下文信息
23      * @param msg 接收的消息
24      */
25     

你可能感兴趣的:(译 3个netty5的例子,简单介绍netty的用法)