Skip to content

Commit 9e957c9

Browse files
committed
completed 测试 ChannelHandler
1 parent 6bae36e commit 9e957c9

3 files changed

+184
-0
lines changed
+184
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
测试 ChannelHandler
2+
====
3+
4+
本节,将使用 EmbeddedChannel 来测试 ChannelHandler
5+
6+
### 测试入站消息
7+
8+
我们来编写一个简单的 ByteToMessageDecoder 实现,有足够的数据可以读取时将产生固定大小的包,如果没有足够的数据可以读取,则会等待下一个数据块并再次检查是否可以产生一个完整包。
9+
10+
如图所示,它可能会占用一个以上的“event”以获取足够的字节产生一个数据包,并将它传递到 ChannelPipeline 中的下一个 ChannelHandler,
11+
12+
![](../images/Figure 10.2 Decoding via FixedLengthFrameDecoder.jpg)
13+
14+
Figure 10.2 Decoding via FixedLengthFrameDecoder
15+
16+
实现如下:
17+
18+
Listing 10.1 FixedLengthFrameDecoder implementation
19+
20+
public class FixedLengthFrameDecoder extends ByteToMessageDecoder { //1
21+
22+
private final int frameLength;
23+
24+
public FixedLengthFrameDecoder(int frameLength) { //2
25+
if (frameLength <= 0) {
26+
throw new IllegalArgumentException(
27+
"frameLength must be a positive integer: " + frameLength);
28+
}
29+
this.frameLength = frameLength;
30+
}
31+
32+
@Override
33+
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
34+
if (in.readableBytes() >= frameLength) { //3
35+
ByteBuf buf = in.readBytes(frameLength);//4
36+
out.add(buf); //5
37+
}
38+
}
39+
}
40+
41+
42+
1. 继承 ByteToMessageDecoder 用来处理入站的字节并将他们解码为消息
43+
2. 指定产出的帧的长度
44+
3. 检查是否有足够的字节用于读到下个帧
45+
4. 从 ByteBuf 读取新帧
46+
5. 添加帧到解码好的消息 List
47+
48+
下面是单元测试的例子,使用 EmbeddedChannel
49+
50+
Listing 10.2 Test the FixedLengthFrameDecoder
51+
52+
public class FixedLengthFrameDecoderTest {
53+
54+
@Test //1
55+
public void testFramesDecoded() {
56+
ByteBuf buf = Unpooled.buffer(); //2
57+
for (int i = 0; i < 9; i++) {
58+
buf.writeByte(i);
59+
}
60+
ByteBuf input = buf.duplicate();
61+
62+
EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3)); //3
63+
Assert.assertFalse(channel.writeInbound(input.readBytes(2))); //4
64+
Assert.assertTrue(channel.writeInbound(input.readBytes(7)));
65+
66+
Assert.assertTrue(channel.finish()); //5
67+
ByteBuf read = (ByteBuf) channel.readInbound();
68+
Assert.assertEquals(buf.readSlice(3), read);
69+
read.release();
70+
71+
read = (ByteBuf) channel.readInbound();
72+
Assert.assertEquals(buf.readSlice(3), read);
73+
read.release();
74+
75+
read = (ByteBuf) channel.readInbound();
76+
Assert.assertEquals(buf.readSlice(3), read);
77+
read.release();
78+
79+
Assert.assertNull(channel.readInbound());
80+
buf.release();
81+
}
82+
83+
84+
@Test
85+
public void testFramesDecoded2() {
86+
ByteBuf buf = Unpooled.buffer();
87+
for (int i = 0; i < 9; i++) {
88+
buf.writeByte(i);
89+
}
90+
ByteBuf input = buf.duplicate();
91+
92+
EmbeddedChannel channel = new EmbeddedChannel(new FixedLengthFrameDecoder(3));
93+
Assert.assertFalse(channel.writeInbound(input.readBytes(2)));
94+
Assert.assertTrue(channel.writeInbound(input.readBytes(7)));
95+
96+
Assert.assertTrue(channel.finish());
97+
ByteBuf read = (ByteBuf) channel.readInbound();
98+
Assert.assertEquals(buf.readSlice(3), read);
99+
read.release();
100+
101+
read = (ByteBuf) channel.readInbound();
102+
Assert.assertEquals(buf.readSlice(3), read);
103+
read.release();
104+
105+
read = (ByteBuf) channel.readInbound();
106+
Assert.assertEquals(buf.readSlice(3), read);
107+
read.release();
108+
109+
Assert.assertNull(channel.readInbound());
110+
buf.release();
111+
}
112+
}
113+
114+
115+
1. 测试增加 @Test 注解
116+
2. 新建 ByteBuf 并用字节填充它
117+
3. 新增 EmbeddedChannel 并添加 FixedLengthFrameDecoder 用于测试
118+
4. 写数据到 EmbeddedChannel
119+
5. 标记 channel 已经完成
120+
6. 读产生的消息并且校验
121+
122+
如上面代码,testFramesDecoded() 方法想测试一个 ByteBuf,这个ByteBuf 包含9个可读字节,被解码成包含了3个可读字节的 ByteBuf。你可能注意到,它写入9字节到通道是通过调用 writeInbound() 方法,之后再执行 finish() 来将 EmbeddedChannel 标记为已完成,最后调用readInbound() 方法来获取 EmbeddedChannel 中的数据,直到没有可读字节。testFramesDecoded2() 方法采取同样的方式,但有一个区别就是入站ByteBuf分两步写的,当调用 writeInbound(input.readBytes(2)) 后返回 false 时,FixedLengthFrameDecoder 值会产生输出,至少有3个字节是可读,testFramesDecoded2() 测试的工作相当于testFramesDecoded()。
123+
124+
### Testing outbound messages
125+
126+
测试的处理出站消息类似于我们刚才看到的一切。这个例子将使用的实现MessageToMessageEncoder:AbsIntegerEncoder
127+
* 当收到 flush() 它将从 ByteBuf 读取4字节整数并给每个执行Math.abs()。
128+
* 每个整数接着写入 ChannelHandlerPipeline
129+
130+
图10.3显示了逻辑。
131+
132+
![](../images/10.3 Encoding via AbsIntegerEncoder.jpg)
133+
134+
Figure 10.3 Encoding via AbsIntegerEncoder
135+
136+
示例如下:
137+
138+
Listing 10.3 AbsIntegerEncoder
139+
140+
public class AbsIntegerEncoder extends MessageToMessageEncoder<ByteBuf> { //1
141+
@Override
142+
protected void encode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> out) throws Exception {
143+
while (in.readableBytes() >= 4) { //2
144+
int value = Math.abs(in.readInt());//3
145+
out.add(value); //4
146+
}
147+
}
148+
}
149+
150+
1. 继承 MessageToMessageEncoder 用于编码消息到另外一种格式
151+
2. 检查是否有足够的字节用于编码
152+
3. 读取下一个输入 ByteBuf 产出的 int 值,并计算绝对值
153+
4. 写 int 到编码的消息 List
154+
155+
在前面的示例中,我们将使用 EmbeddedChannel 测试代码。清单10.4
156+
157+
Listing 10.4 Test the AbsIntegerEncoder
158+
159+
public class AbsIntegerEncoderTest {
160+
161+
@Test //1
162+
public void testEncoded() {
163+
ByteBuf buf = Unpooled.buffer(); //2
164+
for (int i = 1; i < 10; i++) {
165+
buf.writeInt(i * -1);
166+
}
167+
168+
EmbeddedChannel channel = new EmbeddedChannel(new AbsIntegerEncoder()); //3
169+
Assert.assertTrue(channel.writeOutbound(buf)); //4
170+
171+
Assert.assertTrue(channel.finish()); //5
172+
for (int i = 1; i < 10; i++) {
173+
Assert.assertEquals(i, channel.readOutbound()); //6
174+
}
175+
Assert.assertNull(channel.readOutbound());
176+
}
177+
}
178+
179+
1.@Test 标记
180+
2. 新建 ByteBuf 并写入负整数
181+
3. 新建 EmbeddedChannel 并安装 AbsIntegerEncoder 来测试
182+
4. 写 ByteBuf 并预测 readOutbound() 产生的数据
183+
5. 标记 channel 已经完成
184+
6. 读取产生到的消息,检查负值已经编码为绝对值
Loading
Loading

0 commit comments

Comments
 (0)