Netty所需注意细节

来源:互联网 发布:linux服务器配置nodejs 编辑:程序博客网 时间:2024/06/04 18:12

昨天和前天初步过了一下官网,可能后面有点费解,所以今天在这里说说其中存在的个人认为的难理解的点。

首先还是先看一下我的项目整体包含的文件:


先简单介绍一下每个文件的用处:

  1. DiscardServer:我们的服务器入口,定义了管道端口等内容。
  2. TimeClient:我们的客户端入口,同样定义了客户端管道和连接的服务器等。
  3. TimeClientHandler:客户端管道处理逻辑。
  4. TimeDecoder:在客户端正式处理管道数据之前,先对数据进行解码(为什么需要解码呢,往下看)。
  5. TimeEncoder:在服务端正式发送数据之前,先对所需发送的数据进行编码(为什么需要编码,往下看)。
  6. UnixTime:这是我们自定义类,那么很明显了,因为我们想通过Netty发送我们自定义的类来传递内容,也就是所谓的POJO。
这是所有的类文件。那么我们先看第一个DiscardServer:

首先我们注意下1处,这里我们在原来的基础上增加了一个类叫做TimeEncoder ,这个类如下:

其实就是把我们自定义的类的value变量拿出来发送出去。那么这里我们发现他继承的类是ChannelOutboundHandlerAdapter,稍微了解过Netty的朋友看着就眼熟了,outbound出来了。这里我做了测试,这个类的执行要落后于server端的Handler中的ctx.write。那么也就说我们先经过TimeServerHandler中的逻辑处理,然后通过ctx发送出去,如下:
   @Override    public void channelActive(ChannelHandlerContext ctx) throws InterruptedException {        System.out.println("server begin to send ");        ChannelFuture f = ctx.writeAndFlush(new UnixTime());//        ChannelFuture f = ctx.writeAndFlush(Unpooled.copiedBuffer("aaaaa".getBytes()));        f.addListener(ChannelFutureListener.CLOSE);        System.out.println("server finish sending ");    }
但是在发送到client端之前,会经过我们的TimeEncoder类处理。其实这个顺序跟我们在DiscardServer的那张图中1处的设置有关,outbound类型的类在pipeline中倒着执行,放入的越靠前,执行的越晚。所以到这里,大家应该就知道我们为什么加入这一个类了。
同理,TimeDecoder也是如此:


这里有人问了,楼主你被打脸了,人家这边是ByteToMessageDecoder,莫急,往下看:
我们看到,其实他继承的还是ChannelInboundHandlerAdapter类。

我们还是来看之前DiscardServer这张图中的2处,这句话加入有什么用处呢,去掉会怎么样呢,其实很简单,我做过测试,去掉后server端直接就退出了,根本不等ChannelFuture中的管道执行是否完毕。所以我理解,这句话就是跟上面的英文字面意思相近,就是等待我们的管道运行结束,如果管道自己不结束,我们可以人为让他结束就是调用:
f.channel().close();
不过一般不会这么调用,试想你的服务器应该是一直开着等待客户端连接,不可能你会主动关闭服务器,当然,除非你不想活着走出你的公司。

那么可能有人会有另外一个问题,服务器和客户端之间到底是如何工作的。我这里没有深入源码级别,我只能给你讲一下api层面的理解。

首先我们看服务端TimeServerhandler中的这两个方法:
 @Override    public void channelActive(ChannelHandlerContext ctx) throws InterruptedException {        System.out.println("server begin to send ");        ChannelFuture f = ctx.writeAndFlush(new UnixTime());//        ChannelFuture f = ctx.writeAndFlush(Unpooled.copiedBuffer("aaaaa".getBytes()));        f.addListener(ChannelFutureListener.CLOSE);        System.out.println("server finish sending ");    }    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        ByteBuf b = (ByteBuf) msg;        while (b.isReadable()) {            System.out.println((char) b.readByte());        }    }
channelActive在有客户端连接到服务端的时候执行,channelRead在收到客户端发送过来的消息后执行。那么我们先看第一个,当有客户端链接的时候,我们往客户端发送内容,返回一个ChannelFuture,这个怎么类是什么?看一下文档:

我不详细翻译,其实说白了就是一个未来会返回的结果,如果你调用早了,他可能还没有运行完,晚点调用可能就得到想要的结果了,所以他有很多办法去判断结果的状态,比如做没做完isDone等。然后下面我加入了监听,这个监听的意思就是说:关闭通道。其实这一系列操作用一个场景描述:一个寡妇住的屋子(Server),一个单身汉(client)。单身汉敲寡妇屋子(connect),寡妇听到敲门打开门看见汉子脸一红大喊一声死鬼然后关门。然后这个门就关上了,汉子收到了信息,但是如果汉子再想跟寡妇聊天就不行了,门关了。所以这个场景也就是client端收到消息后就无法往服务端发送消息了。所以我们在服务端读到消息后就可以关闭我们的通道了:
 @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        System.out.println("I am in client channel read");        UnixTime m = (UnixTime) msg;        System.out.println(m);//        ctx.writeAndFlush(Unpooled.copiedBuffer("aaaaa".getBytes()));        ctx.close();        System.out.println("client read close");    }
这里有个点要注意,就是我注释的那一行,如果有朋友跟我一样直接发送字符串对方如何都收不到,那么就是这句话的原因,我们需要把我们的字符串放入一个巨大的缓存中,这样才能发送出去,我之前就是把这茬忘了,浪费了一个多小时。

好,Netty的基本使用基本就到此结束了,希望对大家有帮助。

0 0
原创粉丝点击