转载自:https://blog.csdn.net/FishSeeker/article/details/78447684

实验过,例如channle的handler里有很多个outhandler,在out里面写writeAndFlush要使用ctx.writeAndFlush,

因为channle.WriteAndFlush是这样执行的out1->out2->out3->out4

假设在out3的时候使用channel.Write,那么这个处理又从out1开始,陷入死循环

Netty中ctx.writeAndFlush与ctx.channel().writeAndFlush的区别

最近在写netty相关代码,发现writeAndFlush这个方法既可以在ctx上调用,也可以在channel上调用,这两者有什么区别呢,于是就做了一个小实验。具体的代码在最后

Client端
client的handler
这次我们主要在服务端进行实验,因此client端就简单构造一个handler用来接收发来的信息并传送回去,以形成和server通信的态势。

public class C_I_1 extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Number n = (Number)msg;
System.out.println("C in 111 get num = " + n.getNum());
n.add();
ctx.writeAndFlush(n);
}
}
1
2
3
4
5
6
7
8
9
这里构建了一个Number实体类,就是存一个数而已。add方法就是让类的数加一。client的handler就是这个了。

client构建
这就是日常的netty客户端的构建方法,具体的怎么绑定什么的不讲了。然后我们在连接服务端之后向服务端发出一个数字1

public class Client {
static String host = "127.0.0.1";
static int port = 10010;
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY,true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//这里一定要加入这两个类,是用来给object编解码的,如果没有就无法传送对象
//并且,实体类要实现Serializable接口,否则也无法传输
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE,
ClassResolvers.weakCachingConcurrentResolver(null))); // 最大长度
ch.pipeline().addLast(new C_I_1());
}
});
try {
Number n = new Number();
n.setNum(1);
ChannelFuture f =b.connect(host,port).sync();
f.channel().writeAndFlush(n);
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Server端
Server的handler就稍微多一点,为了验证ctx和channel的writeAndFlush到底有什么不同,我们决定建立四个Handler,两个out,两个In,然后交换它们的顺序来看效果。

handler
这里的handler就是接受一个Number类,然后让这个数加一再进行下一步操作。

inboundhandler
public class S_I_1 extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Number n = (Number)msg;
System.out.println("S in 111 get num = " + n.getNum());
n.add();
ctx.fireChannelRead(n);
//ctx.channel().writeAndFlush(n);
}
}
1
2
3
4
5
6
7
8
9
10
outboundhandler
public class S_O_1 extends ChannelOutboundHandlerAdapter {

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Number n = (Number)msg;
System.out.println("S out 111 get num = " + n.getNum());
n.add();
ctx.writeAndFlush(n);
}
}
1
2
3
4
5
6
7
8
9
10
服务器构建
public class Server {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(),workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//这个object转换的如果不放在前面会在发送的时候找不到out
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(Integer.MAX_VALUE,
ClassResolvers.weakCachingConcurrentResolver(null))); // 最大长度
ch.pipeline().addLast(new S_I_1());
ch.pipeline().addLast(new S_O_1());
ch.pipeline().addLast(new S_I_2());
ch.pipeline().addLast(new S_O_2());
}
});
try {
ChannelFuture f = b.bind(10010).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
实验
先说实验结论就是ctx的writeAndFlush是从当前handler直接发出这个消息,而channel的writeAndFlush是从整个pipline最后一个outhandler发出。怎么样,是不是很抽象,下面画图来看一看:

啊,首先解释一下这个图,黑色的是inhandler,红色的是outhandler,前面圆形的是编解码器,必须放在pipline的最前头,否则会让信息发不出去。然后,连接建立之后,in接收到一个数1,选择ctx的writeAndFlush,那么这个数,就会直接从圆形的out出去,因为我们的结论说了,就是从当前的handler直接发出去这个消息。如果使用ctx.channel().writeAndFlush()呢,就会让这个数从红色的2开始发送,经过红色1,再发出去。

让我们看一看另一种情况:

这种情况下,我们让黑色1接收到信息之后fire到黑色2,然后让黑色2把信息writeAndFlush出去,如果使用ctx.writeAndFlush(),那么这个信息就会经过红色1而不经过红色2,如果使用ctx.channel().writeAndFlush()就会从pipline的尾部,也就是红色2开始,经过红色1发出去。

下午写代码的时候突然想到了这种情况,就是在红色的2里,如果用channel的writeAndFlush会有什么样的结果。实验之后发现,是一个死循环,2通过channel的writeAndFlush把消息送回pipline尾部,然后自己获得这个消息,再送回尾部,这样永远都发不出了,这一定要注意哦

好的,就是这样了。
---------------------
作者:FishSeeker
来源:CSDN
原文:https://blog.csdn.net/FishSeeker/article/details/78447684
版权声明:本文为博主原创文章,转载请附上博文链接!

netty之handler write的更多相关文章

  1. 7.Netty中 handler 的执行顺序

    1.Netty中handler的执行顺序 Handler在Netty中,无疑占据着非常重要的地位.Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码.拦截 ...

  2. netty学习--handler传递

    在netty中的处理链pipeline中,事件是按顺序传递的,把自己拟人为netty程序,针对进来(inbound)的请求,会从head开始,依次往tail传递. pipeline采用了链表结构,he ...

  3. netty(七) Handler的执行顺序

    Handler在netty中,无疑占据着非常重要的地位.Handler与Servlet中的filter很像,通过Handler可以完成通讯报文的解码编码.拦截指定的报文.统一对日志错误进行处理.统一对 ...

  4. netty之handler read

    有时候会有一系列的处理in的handler,使用fireChannelRead处理传递 转载自https://blog.csdn.net/u011702633/article/details/8205 ...

  5. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  6. Netty之有效规避内存泄漏

    有过痛苦的经历,特别能写出深刻的文章 —— 凯尔文. 肖 直接内存是IO框架的绝配,但直接内存的分配销毁不易,所以使用内存池能大幅提高性能,也告别了频繁的GC.但,要重新培养被Java的自动垃圾回收惯 ...

  7. 【Netty学习】 ChannelInitializer 学习

    ChannelInitializer在Netty中是一个很重要的东西.也是4.x版本中用户接触比较多的一个类 它本身是继承ChannelInboundHandlerAdapter的.实现Channel ...

  8. 【Netty学习】Netty 4.0.x版本和Flex 4.6配合

    笔者的男装网店:http://shop101289731.taobao.com .冬装,在寒冷的冬季温暖你.新品上市,环境选购 =================================不华丽 ...

  9. 【转】Netty那点事(三)Channel中的Pipeline

    [原文]https://github.com/code4craft/netty-learning/blob/master/posts/ch3-pipeline.md Channel是理解和使用Nett ...

随机推荐

  1. unity探索者之socket传输protobuf字节流(四)

    版权声明:本文为原创文章,转载请声明http://www.cnblogs.com/unityExplorer/p/7027659.html 上篇已经把socket的传输说的差不多了,这篇主要是说说断线 ...

  2. Java之reflection(反射机制)——通过反射操作泛型,注解

    一.反射操作泛型(Generic) Java采用泛型擦除机制来引入泛型.Java中的泛型仅仅是给编译器Javac使用的,确保数据的安全性和免去强制类型转换的麻烦.但是编译一旦完成,所有和泛型有关的类型 ...

  3. Python语言中的关键字(自己做的读书笔记)

    电脑配置:联想笔记本电脑 windows8系统 Python版本:2.7.8 本文章撰写时间:2015.1.1 作者:陈东陈 阅读说明: 1.本文都是先解释,后放图片: 2.文中斜体部分要么为需要输入 ...

  4. Java程序员值得拥有的TreeMap指南

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个有颜值却假装靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题 ...

  5. 通过索引优化sql

    sql语句的优化最重要的一点就是要合理使用索引,下面介绍一下使用索引的一些原则: 1.最左前缀匹配原则.mysql会一直向右匹配直到遇到范围查询(>.<.between.like)就停止匹 ...

  6. 学习一下 JVM (二) -- 学习一下 JVM 中对象、String 相关知识

    一.JDK 8 版本下 JVM 对象的分配.布局.访问(简单了解下) 1.对象的创建过程 (1)前言 Java 是一门面向对象的编程语言,程序运行过程中在任意时刻都可能有对象被创建.开发中常用 new ...

  7. 调试 Ingress Nginx

    由于 Ingress Nginx 是最受欢迎的 Ingress 控制器,因此接下来我们将介绍一些有关调试 ingress-nginx 的技巧. 调试 Ingress Nginx Ingress-ngi ...

  8. centos6.8上安装部署 jhipster-registry

    必备环境:jdk8,git,maven 1.安装nodejs #由于采用编译的方式很容易出现一些意外的惊喜,所以我们这儿直接用yum命令安装 #1.查看nodejs版本(命令中不要加 -y 如果版本不 ...

  9. 面经手册 · 第8篇《LinkedList插入速度比ArrayList快?你确定吗?》

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你以为考你个数据结构是要造火箭? 汽车75马力就够奔跑了,那你怎么还想要2.0涡轮+ ...

  10. 557反转字符串中的单词III

    class Solution: # 定义一个反转字符串的函数. def str_rever(self,s): length = len(s) s1 = '' for index in range(le ...