本文目的:测试数据在ChannelPipeline中的流经顺序及状态。

先看本文的测试代码:

AdditionalInBoundHandler:入站处理器,不做任何处理,只是在响应读事件时打印用来观察,并继续通过fireChannelRead传递读事件。

public class AdditionalInBoundHandler extends ChannelInboundHandlerAdapter {
private String name; public AdditionalInBoundHandler(String name){
this.name = name;
} @Override
public void channelRead(ChannelHandlerContext ctx, Object msg){
System.out.println("go through additional InBoundHandler[" + name + "]; msg type[" + msg.getClass() + "]");
ctx.fireChannelRead(msg);
}
}

BigIntegerDecoder:解码器,用来读取Bytebuf中的字节数据,解码成BigInter对象。

public class BigIntegerDecoder extends ByteToMessageDecoder {

    @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
System.out.println("BigIntegerDecoder");
// Wait until the length prefix is available.
if (in.readableBytes() < 4) {
return;
}
in.markReaderIndex(); int length = in.readInt();
if (in.readableBytes() < length){
in.resetReaderIndex();
return;
}
byte[] data = new byte[length];
in.readBytes(data); out.add(new BigInteger(data));
}

NettyServerHandler:将读到的BigInteger加1并写出。

public class NettyServerHandler extends SimpleChannelInboundHandler<BigInteger> {

    @Override
public void channelRead0(ChannelHandlerContext ctx, BigInteger msg) throws Exception {
System.out.println("NettyServerHandler");
ctx.writeAndFlush(msg.add(BigInteger.ONE));
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}

NumberEncoder:将BigInteger对象编码成字节数字,由ByteBuf写出。

public class NumberEncoder extends MessageToByteEncoder<Number> {

    @Override
protected void encode(ChannelHandlerContext ctx, Number msg, ByteBuf out) {
System.out.println("NumberEncoder");
// Convert to a BigInteger first for easier implementation.
BigInteger v;
if (msg instanceof BigInteger) {
v = (BigInteger) msg;
} else {
v = new BigInteger(String.valueOf(msg));
} // Convert the number into a byte array.
byte[] data = v.toByteArray();
int dataLength = data.length; // Write a message.
out.writeInt(dataLength); // data length
out.writeBytes(data); // data
}
}

AdditionalOutboundHandler:出站处理器,不对数据做任何处理,直接写出,只是简单增加了打印功能,方便测试。

public class AdditionalOutBoundHandler extends ChannelOutboundHandlerAdapter {
private String name; public AdditionalOutBoundHandler(String name) {
this.name = name;
} @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("go through additional outbound handler[" + name + "];msg type [" + msg.getClass() + "]");
ctx.write(msg, promise);
}
}

测试代码:channelHandlerTest。通过EmbeddedChannel进行测试。

public class ChannelHandlerTest {

    @Test
public void channelHandlerTest(){
ByteBuf buf = Unpooled.buffer(); EmbeddedChannel embeddedChannel = new EmbeddedChannel(); ChannelPipeline pipeline = embeddedChannel.pipeline(); pipeline.addLast(new AdditionalInBoundHandler("in handler 1"));
pipeline.addLast(new AdditionalInBoundHandler("in handler 2"));
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder()); pipeline.addLast(new AdditionalOutBoundHandler("out handler 1"));
pipeline.addLast(new AdditionalInBoundHandler("in handler 3"));
pipeline.addLast(new NettyServerHandler());
pipeline.addLast(new AdditionalInBoundHandler("in handler 4"));
pipeline.addLast(new AdditionalOutBoundHandler("out handler 2")); byte[] bytes = new byte[]{0x01, 0x02, 0x03, 0x04};
BigInteger bi = new BigInteger(bytes); buf.writeInt(4);
buf.writeBytes(bytes); //因为nettyServerHandler fire了写事件,因此channelpipeline尾部没数据可读
assertTrue(!embeddedChannel.writeInbound(buf)); ByteBuf readBuf = embeddedChannel.readOutbound();
int length = readBuf.readInt();
bytes = new byte[length];
readBuf.readBytes(bytes);
System.out.println(new BigInteger(bytes));
}
}

我们可以看到ChannelPipeline中的顺序是头到尾:InBound1(入) -> InBound2(入) -> BigIntegerDecoder(入) -> NumberEncoder(出) -> OutBound1(出) -> InBound3(入) -> NettyServerHandler(入) -> InBound4(入) -> OutBound2(出)。

执行测试代码,我们可以看到控制台中的输出是:

我们通过一张流程图片更清晰的看一下整个流经过程以及数据的状态:

图上的箭头很清楚的表示了数据流经的过程,[XXX则注释出了此时的数据对象。

入站数据沿着ChannelPipeline的头至尾依次寻找下一个可以处理对应消息的处理器。

出战数据则会沿着ChannelPipeline的尾至头依次寻找下一个可以处理对应消息的处理器。

比如为什么数据没有流经InBoundHandler4和OutBoundHandler2?

因为NettyServerHandler发出了一个写事件,因此数据从尾至头的方法找下一个可以处理写消息的处理器,即OutBoundHandler1,在顺延下去。

Netty(二):数据在ChannelPipeline中的流经的更多相关文章

  1. SpringMVC(二)返回值设置、数据在域中的保存与SpringMVC案例

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.返回值的设置 1.返回 String [1]返回 String 默认情况 @RequestMappi ...

  2. Scrapy基础(十二)————异步导出Item数据到Mysql中

    异步导出数据到Mysql中 上次说过从Item中同步写入数据库,因为网络的下载速度和数据库的I/O速度是不一样的所以有可能会发生下载快,但是写入数据库速度慢,造成线程的堵塞:关于堵塞和非堵塞,同步和异 ...

  3. 【Netty】ChannelHandler和ChannelPipeline

    一.前言 前面学习了Netty的ByteBuf,接着学习ChannelHandler和ChannelPipeline. 二.ChannelHandler和ChannelPipeline 2.1 Cha ...

  4. 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式

    Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...

  5. 在dubbo的一端,看Netty处理数据包,揭网络传输原理

    如今,我们想要开发一个网络应用,那是相当地方便.不过就是引入一个框架,然后设置些参数,然后写写业务代码就搞定了. 写业务代码自然很重要,但是你知道: 你的数据是怎么来的吗?通过网络传输过来的呗. 你知 ...

  6. 自顶向下深入分析Netty(七)--ChannelPipeline和ChannelHandler总述

    自顶向下深入分析Netty(七)--ChannelPipeline和ChannelHandler总述 自顶向下深入分析Netty(七)--ChannelPipeline源码实现 自顶向下深入分析Net ...

  7. Netty(二):如何处理io请求?

    文接上一篇.上篇讲到netty暴露一个端口出来,acceptor, handler, pipeline, eventloop 都已准备好.但是并没体现其如何处理接入新的网络请求,今天我们就一起来看看吧 ...

  8. 一文搞懂 Netty 发送数据全流程 | 你想知道的细节全在这里

    欢迎关注公众号:bin的技术小屋,如果大家在看文章的时候发现图片加载不了,可以到公众号查看原文 本系列Netty源码解析文章基于 4.1.56.Final版本 在<Netty如何高效接收网络数据 ...

  9. (转)原始图像数据和PDF中的图像数据

    比较原始图像数据和PDF中的图像数据,结果见表1.1.表1.1中各种“解码器”的解释见本文后续的“PDF支持的图像格式”部分,“PDF中的图像数据”各栏中的数据来自开源的PdfView.如果您有兴趣查 ...

随机推荐

  1. 为什么要学习Oracle技术?

    为什么要学习Oracle技术? 众所周知,Oracle占据着企业数据库领域超过48.1%的市场份额,成为高端企业数据库软件的绝对领导者.随着时间的推移,企业数据库的规模不断扩大,富有经验的资深Orac ...

  2. c期末笔记(1)

    运算符 1.i++与++i的细微区别 i++与++i 和i++放在一个语句中,则i原本的值先被利用.语句结束后,i的值加一. i的原始值失效,直接加一. 2.int加法 整形数据(int)加上任何类型 ...

  3. 大数据hadoop安装

    hadoop集群搭建--CentOS部署Hadoop服务 在了解了Hadoop的相关知识后,接下来就是Hadoop环境的搭建,搭建Hadoop环境是正式学习大数据的开始,接下来就开始搭建环境!我们用到 ...

  4. Linux 下普通用户切换root超级管理员用户的几种方法

    1.在命令行下输入:sudo su ,之后会提示你输入密码 2.此时输入你之前设定的密码既可: 3.但有时会提示你该普通用户不在sudoers文件里 4.此时可以使用以下命令来切换root用户权限:s ...

  5. Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j)

    Java日志管理:Logger.getLogger()和LogFactory.getLog()的区别(详解Log4j) 博客分类: Java综合   第一.Logger.getLogger()和Log ...

  6. Python 文件拼接

    # -*- coding:utf-8 -*- import re import csv file = open('make_setup.cfg', 'w+') with open("tyb. ...

  7. layoutInflater参数解析与源码分析

    关于LayoutInflater方法,无论是在listview的适配器中,还是在动态添加view的时候,都会出现它的身影,最开始我在看<第一行代码>时,不知道这个方法实际的参数到底指的是什 ...

  8. Xshell远程连接Linux系统

    一般来说我们连接Linux,会使用到一些远程连接工具 比如:Xshell和Xftp Xshell:远程连接linux系统 Xftp:远程在Linux系统中上传或下载文件 Xshell和Xftp百度云链 ...

  9. F - Distinct Numbers

    链接:https://atcoder.jp/contests/abc143/tasks/abc143_f 题解:开两个数组,其中一个arr用来保存每个元素出现的次数,同时再开一个数组crr用来保存出现 ...

  10. Mysql数据导入导出功能(设置及使用)

    使用Mysql自带的outfile语法,将查询结果导成excel格式. 1.OUTFILE介绍及常见问题解决: )查询数据导出成csv 直接使用mysql导出csv方法 我们可以使用 into out ...