1. 程式人生 > >netty之handler write

netty之handler write

轉載自: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
版權宣告:本文為博主原創文章,轉載請附上博文連結!