请选择 进入手机版 | 继续访问电脑版
搜索
房产
装修
汽车
婚嫁
健康
理财
旅游
美食
跳蚤
二手房
租房
招聘
二手车
教育
茶座
我要买房
买东西
装修家居
交友
职场
生活
网购
亲子
情感
龙城车友
找美食
谈婚论嫁
美女
兴趣
八卦
宠物
手机

netty 4.x用户使用指南

[复制链接]
查看: 56|回复: 0

2万

主题

2万

帖子

7万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
70839
发表于 2020-1-17 02:52 | 显示全部楼层 |阅读模式
引言
题目  现在我们操纵通用的利用步伐或库来相互通讯。例如,我们经常操纵HTTP客户机从web办事器检索信息,并经过web办事挪用远程进程挪用。但是,通用协议或实在现偶然不能很好地停止扩大。这就像我们倒霉用通用HTTP办事器来交换庞大的文件、电子邮件消息和近乎实时的消息(如财政信息和多人游戏数据)一样。所必要的是一个高度优化的协议实现,专门用于一个出格目标。例如,您大要盼望实现一个针对基于ajax的聊天利用步伐、媒体流或大文件传输停止优化的HTTP办事器。您以致可以筹划和实现一个完全按照您的必要量身定制的全新协议。另一个不成制止的情况是,您必须处置赏罚遗留的专有协议,以确保与旧系统的互操纵性。在这类情况下,严重的是在不竭送结果利用步伐的安定性和性能的情况下,我们能以多快的速度实现该协议。
打点计划  Netty项目旨在供给异步变乱驱动的收集利用步伐框架和工具,以快速开辟可保护的高性能·高可伸缩性协议办事器和客户端。
  换句话说,Netty是一个NIO客户端办事器框架,它支持协议办事器和客户端等收集利用步伐的快速轻松开辟。它极大地简化和简化了TCP和UDP套接字办事器开辟等收集编程。
  “快速和简单”并不意味着终极的利用步伐将蒙受可保护性或性能题目标影响。Netty是按照从很多协议(如FTP、SMTP、HTTP以及各类基于二进制和文本的遗留协议)的实现中学到的履历尽心筹划的。是以,Netty乐成地找到了一种无需妥协便可以轻松实现开辟、性能、安定性和灵活性的方式。  一些用户大要已经发现了其他宣称具有类似上风的收集利用步伐框架,您大要想晓得是什么使Netty与他们如此分歧。答案是它所建立的哲学。Netty旨在从一路头就为您供给最舒服的API和实现体验。它不是什么无形的工具,但你会意想到这类哲学将使你的保存更轻易,当你读这本指南和玩Netty。 预备起头  本章将先容Netty的焦点结构,并经过简单的示例让您快速入门。当你在这一章竣事的时候,你将可以也许立即在Netty上编写一个客户端和一个办事器。
  假如您更喜勤进修自顶向下的方式,那末您大要盼望从第2章——系统结构概述起头,然后回到这里。 在起头之前  运转本章示例的最低要求只要两个;Netty和JDK 1.6或更高版本的最新版本。最新版本的Netty可在项目下载页面获得。要下载切确版本的JDK,请参考您首选的JDK供给商的网站。 编写Discard办事  天下上最简单的协议不是“你好,天下!”,而是DISCARD。它是一种协议,在没有任何响应的情况下抛弃任何吸收到的数据。
  要实现抛弃协议,唯一必要做的就是疏忽一切吸收到的数据。让我们间接从处置赏罚步伐实现起头,它处置赏罚Netty天生的I/O变乱。
  1. package io.netty.example.discard;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;/** * Handles a server-side channel. */public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)        // Discard the received data silently.抛弃吸收到的数据        ((ByteBuf) msg).release(); // (3)    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)        // Close the connection when an exception is raised.        cause.printStackTrace();        ctx.close();    }}
复制代码
注:1、DiscardServerHandler扩大了ChannelInboundHandlerAdapter,它是ChannelInboundHandler的一个实现。ChannelInboundHandler供给可以覆盖的各类变乱处置赏罚步伐方式。现在,只必要扩大ChannelInboundHandlerAdapter,而不是自己实现处置赏罚步伐接口就充沛了。       2、我们在这里重写channelRead()变乱处置赏罚步伐方式。当从客户机吸收到新数据时,将操纵吸收到的消息挪用此方式。在本例中,吸收到的消息典范是ByteBuf。       3、要实现抛弃协议,处置赏罚步伐必须疏忽吸收到的消息。ByteBuf是一个援用计数工具,必须经过release()方式显式地开释它。请记着,处置赏罚步伐有义务开释传递给处置赏罚步伐的任何援用计数工具。凡是,channelRead()处置赏罚步伐方式是这样实现的:
  1. @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {    try {        // Do something with msg    } finally {        ReferenceCountUtil.release(msg);    }}
复制代码
  4、很是捕捉()变乱处置赏罚步伐方式在Netty由于I/O毛病或处置赏罚步伐实现由于在处置赏罚变乱时抛出很是而激发很是时被一次性挪用。在大大都情况下,应当对捕捉的很是停止日志记载,并封闭其关联的通道,尽管此方式的实现大要因您盼望怎样处置赏罚很是情况而有所分歧。例如,您大要盼望在封闭毗连之前发送带有毛病代码的响应消息。  到现在为止还不错。我们已经实现了烧毁办事器的前一半。现在剩下的是编写main()方式,该方式操纵DiscardServerHandler启动办事器。
  1. import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelOption;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;    /** * Discards any incoming data.丢掉一切进来的消息 */public class DiscardServer {        private int port;        public DiscardServer(int port) {        this.port = port;    }        public void run() throws Exception {        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) 该工具相当于Socket中操纵一个线程专门用户监听一个socket端口,然后将监听到的socket工具传入另一工具        EventLoopGroup workerGroup = new NioEventLoopGroup();// 该工具相当于Socket中对于每个socket毗连都都零丁开辟了一个线程停止数据分析出处置赏罚        try {            ServerBootstrap b = new ServerBootstrap(); // (2)            b.group(bossGroup, workerGroup)             .channel(NioServerSocketChannel.class) // (3)             .childHandler(new ChannelInitializer() { // (4)                 @Override                 public void initChannel(SocketChannel ch) throws Exception {                     ch.pipeline().addLast(new DiscardServerHandler());                 }             })             .option(ChannelOption.SO_BACKLOG, 128)          // (5)             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)                // Bind and start to accept incoming connections.            ChannelFuture f = b.bind(port).sync(); // (7)                // Wait until the server socket is closed.            // In this example, this does not happen, but you can do that to gracefully            // shut down your server.            f.channel().closeFuture().sync();        } finally {            workerGroup.shutdownGracefully();            bossGroup.shutdownGracefully();        }    }        public static void main(String[] args) throws Exception {        int port;        if (args.length > 0) {            port = Integer.parseInt(args[0]);        } else {            port = 8080;        }        new DiscardServer(port).run();    }}
复制代码
注:1、NioEventLoopGroup是一个处置赏罚I/O操纵的多线程变乱循环。Netty为分歧典范的传输供给了各类EventLoopGroup实现。在本例中,我们正在实现一个办事器端利用步伐,是以将操纵两个NioEventLoopGroup。第一个,凡是被称为“老板”,担任进入的毗连。第二个凡是称为“worker”,在boss担任毗连并将担任的毗连注册给worker时,它将处置赏罚已担任毗连的流量。操纵几多线程以及怎样将它们映照到建立的通道取决于EventLoopGroup实现,以致可以经过机关函数停止设备。
        2、ServerBootstrap是一个设购置事器的助手类。您可以间接操纵Channel设购置事器。可是,请留意,这是一个冗杂的进程,在大大都情况下不必要这样做。
        3、在这里,我们指定操纵NioServerSocketChannel类,该类用于实例化一个新通道以担任传入毗连。
        4、这里指定的处置赏罚步伐将总是由新担任的通道停止盘算。ChannelInitializer是一个用于帮助用户设备新通道的出格处置赏罚步伐。您很大要盼望经过增加一些处置赏罚步伐(如DiscardServerHandler)来实现收集利用步伐来设备新通道的ChannelPipeline。随着利用步伐变得复杂,您大要会向管道中增加更多的处置赏罚步伐,并终极将这个匿名类提取到顶级类中。        5、您还可以设备特定于通道实现的参数。我们正在编写一个TCP/IP办事器,所以我们可以设备套接字选项,如tcpNoDelay和keepAlive。请参考ChannelOption的apidocs以及特定的ChannelConfig实现,以获得关于支持的ChannelOptions的概述。        6、你留意到option()和childOption()吗?option()用于担任传入毗连的NioServerSocketChannel。childOption()是父ServerChannel担任的通道,在本例中是NioServerSocketChannel        7、我们预备好了。剩下的就是绑定到端口并启动办事器。在这里,我们绑定到呆板中一切NICs(收集接口卡)的端口8080。现在,您可以按照必要屡次挪用bind()方式(操纵分歧的绑定地点)。
  恭喜你!你刚刚在Netty上完成了你的第一个办事器。 检察吸收到的数据  现在我们已经编写了第一个办事器,我们必要测试它能否真正工作。测试它的最简双方式是操纵telnet命令。例如,您可以在命令行中输入telnet localhost 8080并输入一些内容。可是,我们可以说办事器工作得很好吗?我们没法真正晓得这一点,由于它是一个抛弃办事器。你不会获得任何回应。为了证实它确切有用,让我们点窜办事器来打印它收到的内容。我们已经晓得,每当吸收到数据时城市挪用channelRead()方式。让我们将一些代码放入DiscardServerHandler的channelRead()方式中:
  1. @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {    ByteBuf in = (ByteBuf) msg;    try {        while (in.isReadable()) { // (1)            System.out.print((char) in.readByte());            System.out.flush();        }    } finally {        ReferenceCountUtil.release(msg); // (2)    }}
复制代码
注: 1、这个低效的循环现实上可以简化为:System.out.println(in.toString(io.net .util. charsetutil.us_ascii))        2、大要,您可以在这里实行in.release()。 题目编写ECHO办事  到现在为止,我们不停在操纵数据,而没有做出任何响应。但是,办事器凡是应当响应请求。让我们进修怎样经过实现ECHO协议向客户端写入响应消息,其中一切吸收到的数据都被发回。
  与我们在前几节中实现的Discards办事器的唯一区分是,它将吸收到的数据发送回办事器,而不是将吸收到的数据打印到控制台。是以,再次点窜channelRead()方式就充沛了:
  1. @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {    ctx.write(msg); // (1)    ctx.flush(); // (2)}
复制代码
注: 1、ChannelHandlerContext工具供给各类操纵,使您可以也许触发各类I/O变乱和操纵。在这里,我们挪用write(Object)以逐字记载吸收到的消息。请留意,我们没有开释吸收到的消息,这与我们在抛弃示例中所做的分歧。这是由于当它被写在网上时,Netty会为你开释它。        2、ctx.write(Object)不会将消息写到线路上。它在内部停止缓冲,然后经过ctx.flush()将其冲到电线上。大要,为了简洁起见,可以挪用ctx.writeAndFlush(msg)。假如您再次运转telnet命令,您将看到办事器返回您发送给它的任何内容。
echo办事器的完整源代码位于刊行版的io.net .example.echo包中。
编写一个时候办事  本节中要实现的协议是TIME协议。与前面的示例分歧的是,它发送一个包含32位整数的消息,而不吸收任何请求,而且在消息发送后封闭毗连。在本例中,您将进修怎样机关和发送消息,以及若何在完成时封闭毗连。由于我们将疏忽任何吸收到的数据,可是在毗连建立后立即发送消息,所以此次我们不能操纵channelRead()方式。相反,我们应当重写channelActive()方式。下面是实现:
  1. package io.netty.example.time;public class TimeServerHandler extends ChannelInboundHandlerAdapter {    @Override    public void channelActive(final ChannelHandlerContext ctx) { // (1)        final ByteBuf time = ctx.alloc().buffer(4); // (2)        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));                final ChannelFuture f = ctx.writeAndFlush(time); // (3)        f.addListener(new ChannelFutureListener() {            @Override            public void operationComplete(ChannelFuture future) {                assert f == future;                ctx.close();            }        }); // (4)    }        @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {        cause.printStackTrace();        ctx.close();    }}
复制代码
注: 1、如前所述,将在建立毗连并预备天生通讯量时挪用channelActive()方式。让我们写一个32位整数,表示这个方式中确当前时候。        2、要发送新消息,我们必要分派一个包含消息的新缓冲区。我们要写一个32位整数,是以我们必要一个ByteBuf,它的容量最少是4字节。经过ChannelHandlerContext.alloc()获得当前的ByteBufAllocator,并分派一个新的缓冲区。        3、像平常一样,我们编写机关好的消息。
             可是等等,抛硬币在那边?在操纵NIO发送消息之前,我们不是已经挪用java.nio.ByteBuffer.flip()吗?ByteBuf没有这样的方式,由于它有两个指针;一个用于读操纵,另一个用于写操纵。当您向ByteBuf写入内容时,写入器索引会增加,而读取器索引不会改变。阅读器索引和写入器索引别离表示消息起头和竣事的位置。
              相反,NIO缓冲区没有供给一种清洁的方式来肯定消息内容在那边起头和竣事,而不挪用flip方式。当您忘记翻转缓冲区时,您将碰到贫苦,由于不会发送任何或不切确的数据。在Netty中不会发生这样的毛病,由于对于分歧的操纵典范,我们有分歧的指针。当你风尚了它,你会发现它让你的保存变得更轻易——一个没有翻转的保存!

              要留意的另一点是ChannelHandlerContext.write()(和writeAndFlush())方式返回ChannelFuture。ChannelFuture表示尚未发生的I/O操纵。这意味着,由于Netty中的一切操纵都是异步的,是以大要还没有实行任何请求的操纵。例如,以下代码大要会在发送消息之前封闭毗连:
  1. Channel ch = ...;ch.writeAndFlush(message);ch.close();
复制代码
  是以,您必要在ChannelFuture完成以后挪用close()方式,这个方式由write()方式返回,当写操纵完成时,它会看护它的侦听器。请留意,close()也大要不会立即封闭毗连,它将返回ChannelFuture。        4、那末,当写请求完成时,我们怎样获得看护?这就像在返回的通道未来中增加一个ChannelFutureListener一样简单。在这里,我们建立了一个新的匿名通道futurelistener,它在操纵完成时封闭通道。              大要,您可以操纵预界说的侦听器简化代码:
  1. f.addListener(ChannelFutureListener.CLOSE);
复制代码
  要测试我们的时候办事器能否按预期工作,您可以操纵UNIX rdate命令:
  1. $ rdate -o  -p
复制代码
  其中是main()方式中指定的端口号,凡是是本田主机。 编写一个时候客户端  与Discard办事器和ECHO办事器分歧,我们必要一个时候协议客户机,由于人不能将32位二进制数据转换为日历上的日期。在本节中,我们将会商怎样确保办事器切确工作,以及怎样操纵Netty编写客户机。
  在Netty中,办事器和客户机之间最大也是唯一的区分是操纵了分歧的指导和通道实现。请检察以下代码:
  1. package io.netty.example.time;public class TimeClient {    public static void main(String[] args) throws Exception {        String host = args[0];        int port = Integer.parseInt(args[1]);        EventLoopGroup workerGroup = new NioEventLoopGroup();                try {            Bootstrap b = new Bootstrap(); // (1)            b.group(workerGroup); // (2)            b.channel(NioSocketChannel.class); // (3)            b.option(ChannelOption.SO_KEEPALIVE, true); // (4)            b.handler(new ChannelInitializer() {                @Override                public void initChannel(SocketChannel ch) throws Exception {                    ch.pipeline().addLast(new TimeClientHandler());                }            });                        // Start the client.            ChannelFuture f = b.connect(host, port).sync(); // (5)            // Wait until the connection is closed.            f.channel().closeFuture().sync();        } finally {            workerGroup.shutdownGracefully();        }    }}
复制代码
注:1、Bootstrap类似于ServerBootstrap,只是它用于非办事器通道,比如客户端通道或无毗连通道。
    2、假如只指定一个EventLoopGroup,它将作为boss组和工作者组操纵。可是,老板员工并不用于客户端    3、与NioServerSocketChannel分歧,NioSocketChannel用于建立客户端通道。    4、留意,这里我们不像操纵ServerBootstrap那样操纵childOption(),由于客户端SocketChannel没有父节点。    5、我们应当挪用connect()方式,而不是bind()方式。如您所见,它与办事器端代码并没有什么分歧。那末ChannelHandler实现呢?它应当从办事器吸收一个32位的整数,将其转换为人类可读的格式,打印翻译后的时候,并封闭毗连:
  1. package io.netty.example.time;import java.util.Date;public class TimeClientHandler extends ChannelInboundHandlerAdapter {    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) {        ByteBuf m = (ByteBuf) msg; // (1)        try {            long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;            System.out.println(new Date(currentTimeMillis));            ctx.close();        } finally {            m.release();        }    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {        cause.printStackTrace();        ctx.close();    }}
复制代码
注:1、在TCP/IP中,Netty将从对等点发送的数据读入ByteBuf。  它看起来很是简单,与办事器端示例没有任何分歧。但是,这个处置赏罚步伐偶然会拒绝启动IndexOutOfBoundsException。我们将鄙人一节会商为什么会发生这类情况。 处置赏罚基于流的传输  套接字缓冲区的一个小警告  在基于流的传输(如TCP/IP)中,吸收到的数据存储在套接字吸收缓冲区中。不幸的是,基于流的传输的缓冲区不是包的行列,而是字节的行列。这意味着,即使您将两个消息作为两个自力的信息包发送,操纵系统也不会将它们视为两个消息,而只是一堆字节。是以,不能保证您所阅读的内容就是您的远程对等者所写的内容。例如,假定一个操纵系统的TCP/IP栈吸收了三个包:
我的关键词 netty 4.x用户利用指南  热门消息 1024228-20190518002123670-1696471420
  由于基于流的协议的一样平常特征,在您的利用步伐中很有大要以以下片断形式阅读它们:
我的关键词 netty 4.x用户利用指南  热门消息 1024228-20190518002255735-1709106391
http://www.waysfocus.com/https://www.cnblogs.com/file:///D:/Documents/My%20Knowledge/temp/fdc5ee1f-a393-4028-b68b-d0329f4f5f7e/128/index_files/9c219686-75a8-4c00-8d3e-3f1dc9a8ab04.png
  是以,不管吸收部分是办事器端照旧客户端,都应当将吸收到的数据碎片整理成利用步伐逻辑可以轻易大白的一个或多个故意义的帧,利用步伐逻辑可以很轻易地大白这些帧。对于上面的例子,吸收到的数据应当像下面这样机关:
我的关键词 netty 4.x用户利用指南  热门消息 1024228-20190518002348268-1583806828
http://www.waysfocus.com/https://www.cnblogs.com/file:///D:/Documents/My%20Knowledge/temp/fdc5ee1f-a393-4028-b68b-d0329f4f5f7e/128/index_files/48e1411d-925c-4411-bebd-556e361993c3.png
第一个打点计划:  现在让我们回到TIME客户端示例。我们在这里碰到一样的题目。32位整数黑白常少许的数据,而且不太大要经常被分段。但是,题目在于它大如果碎片化的,而且随着流量的增加,碎片化的大要性将增加。  简单的打点计划是建立一个内部积累缓冲区,并等待一切4个字节都被吸收到内部缓冲区。以下是TimeClientHandler修复此题目标点窜实现:
  1. package io.netty.example.time;import java.util.Date;public class TimeClientHandler extends ChannelInboundHandlerAdapter {    private ByteBuf buf;        @Override    public void handlerAdded(ChannelHandlerContext ctx) {        buf = ctx.alloc().buffer(4); // (1)    }        @Override    public void handlerRemoved(ChannelHandlerContext ctx) {        buf.release(); // (1)        buf = null;    }        @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) {        ByteBuf m = (ByteBuf) msg;        buf.writeBytes(m); // (2)        m.release();                if (buf.readableBytes() >= 4) { // (3)            long currentTimeMillis = (buf.readUnsignedInt() - 2208988800L) * 1000L;            System.out.println(new Date(currentTimeMillis));            ctx.close();        }    }        @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {        cause.printStackTrace();        ctx.close();    }}
复制代码
注:1、ChannelHandler有两个生命周期监听器方式:handlerAdded()和handlerRemoved()。您可以实行尽情(de)初始化使命,只要它不会长时候阻塞。    2、首先,应将一切收到的数据积累到buf。    3、然后,处置赏罚步伐必须检查buf能否有充沛的数据,在此示例中为4个字节,然后继续实行现实的营业逻辑。否则,channelRead()当更大都据到达时,Netty将再次挪用该方式,终极将累计一切4个字节。 第二种打点计划  固然第一个打点计划已经打点了TIME客户真个题目,但点窜后的处置赏罚步伐看起来并不清洁。设想一个更复杂的协议,它由多个字段组成,例如可变长度字段。您的ChannelInboundHandler实施将很快变得没法保护。  您大要已经留意到,您可以ChannelHandler为a 增加多个ChannelPipeline,是以,您可以将一个单片拆分ChannelHandler为多个模块化,以低落利用步伐的复杂性。例如,您可以拆分TimeClientHandler为两个处置赏罚步伐:

  • TimeDecoder 它触及碎片题目,以及
  • 最初的简单版本TimeClientHandler
  侥幸的是,Netty供给了一个可扩大的类,可以帮助您编写第一个开箱即用的类:
  1. package io.netty.example.time;public class TimeDecoder extends ByteToMessageDecoder { // (1)    @Override    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { // (2)        if (in.readableBytes() < 4) {            return; // (3)        }        out.add(in.readBytes(4)); // (4)    }}
复制代码
注:1、ByteToMessageDecoder是一种实现ChannelInboundHandler,可以很轻易地处置赏罚碎片题目。       2、ByteToMessageDecoderdecode()每当收到新数据时,城市操纵内部保护的积累缓冲区挪用该方式。       3、decode()可以决议不向积累缓冲区中没有充沛数据的地方增加任何内容。当吸收到更大都据时,ByteToMessageDecoder将再次挪用decode()。      4、假如decode()向out增加一个工具,则表示解码器乐成解码一条消息。ByteToMessageDecoder将抛弃积累缓冲区的读取部分。请记着,您不必要解码多个消息。ByteToMessageDecoder将继续挪用decode()方式,直到它没有向out增加任何内容为止。  现在我们有另一个处置赏罚步伐插入到ChannelPipeline中,我们应当点窜TimeClient中的ChannelInitializer实现:
  1. b.handler(new ChannelInitializer() {    @Override    public void initChannel(SocketChannel ch) throws Exception {        ch.pipeline().addLast(new TimeDecoder(), new TimeClientHandler());    }});
复制代码
  假如你是一个爱好冒险的人,你大要想试试ReplayingDecoder,这将解码器变得加倍简单。不外,您必要参考API参考以获得更多信息。
  1. public class TimeDecoder extends ReplayingDecoder {    @Override    protected void decode(            ChannelHandlerContext ctx, ByteBuf in, List out) {        out.add(in.readBytes(4));    }}
复制代码
  此外,Netty供给了开箱即用的解码器,它使您可以也许很是轻易地实现大大都协议,并帮助您制止终极出现难以保护的单块处置赏罚步伐实现。更具体的例子请参考以下包:   
用POJO取代ByteBuf  到现在为止,我们所检查的一切示例都操纵了ByteBuf作为协议消息的严重数据结构。在本节中,我们将改良TIME协议客户端和办事器示例以操纵POJO而不是ByteBuf。在你的ChannelHandler中操纵POJO的上风是不言而喻的; 经过分离ByteBuf从处置赏罚步伐中提取信息的代码,您的处置赏罚步伐变得更易于保护和重用。在TIME客户端和办事器示例中,我们只读取一个32位整数,这不是ByteBuf间接操纵的严重题目。可是,您会发现在实现实在天下协议时必须停止分手。  首先,让我们界说一个名为的新典范UnixTime。
  1. package io.netty.example.time;import java.util.Date;public class UnixTime {    private final long value;        public UnixTime() {        this(System.currentTimeMillis() / 1000L + 2208988800L);    }        public UnixTime(long value) {        this.value = value;    }            public long value() {        return value;    }            @Override    public String toString() {        return new Date((value() - 2208988800L) * 1000L).toString();    }}
复制代码
  我们现在可以点窜它TimeDecoder来发生一个UnixTime而不是一个ByteBuf
  1. @Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) {    if (in.readableBytes() < 4) {        return;    }    out.add(new UnixTime(in.readUnsignedInt()));}
复制代码
  操纵更新的解码器,TimeClientHandler不再操纵ByteBuf
  1. @Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {    UnixTime m = (UnixTime) msg;    System.out.println(m);    ctx.close();}
复制代码
  更简单,更文雅,对吧?可以在办事器端利用类似的技术。我们TimeServerHandler此次更新第一次:
  1. @Overridepublic void channelActive(ChannelHandlerContext ctx) {    ChannelFuture f = ctx.writeAndFlush(new UnixTime());    f.addListener(ChannelFutureListener.CLOSE);}
复制代码
  现在,唯一缺氨赡部分是一个编码器,它的实现ChannelOutboundHandler将一个UnixTime转换为一个ByteBuf。它比编写解码器简单很多,由于编码消息时无需处置赏罚数据包碎片和汇编。
  1. package io.netty.example.time;public class TimeEncoder extends ChannelOutboundHandlerAdapter {    @Override    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {        UnixTime m = (UnixTime) msg;        ByteBuf encoded = ctx.alloc().buffer(4);        encoded.writeInt((int)m.value());        ctx.write(encoded, promise); // (1)    }}
复制代码
注:1、这一行中有很多严重的事变。            首先,我们按原样传递原始文件ChannelPromise,以便当编码数据现实写入线路时,Netty将其标志为乐成或失利。            第二,我们没有挪用ctx.flush()。有一个零丁的处置赏罚步伐方式void flush(ChannelHandlerContext ctx),用于覆盖flush()操纵。  为了进一步简化,您可以操纵MessageToByteEncoder
  1. public class TimeEncoder extends MessageToByteEncoder {    @Override    protected void encode(ChannelHandlerContext ctx, UnixTime msg, ByteBuf out) {        out.writeInt((int)msg.value());    }}
复制代码
  末端一个使命是在TimeServerHandler之前将一个TimeEncoder插入到办事器端ChannelPipeline中,这只是一个简单的练习。 封闭你的利用步伐  封闭一个Netty利用步伐凡是与封闭经过shutdowndowns()建立的一切EventLoopGroups一样简单。当EventLoopGroup完全制止而且属于该组的一切通道都已封闭时看护您,它返回一个Future提要  在本章中,我们快速欣赏了Netty,并演示了若何在Netty上编写完整的收集利用步伐。  在接下来的章节中有关于Netty的更多具体信息。我们还激励您检察io.netty.example包中的Netty示例。  另请留意,社区始终在等待您的题目和想法,以帮助您并按照您的反应不停改良Netty及其文档。
免责声明:假如加害了您的权益,请联系站长,我们会实时删除侵权内容,感谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Copyright © 2006-2014 淄博新闻网-淄博日报 淄博晚报 淄博财经新报 掌中淄博 淄博专业新闻资讯发布网站 版权所有 法律顾问:高律师 客服电话:0791-88289918
技术支持:迪恩网络科技公司  Powered by Discuz! X3.2
快速回复 返回顶部 返回列表