|
| 1 | +测试异常处理 |
| 2 | +==== |
| 3 | + |
| 4 | +有时候传输的入站或出站数据不够,通常这种情况也需要处理,例如抛出一个异常。这可能是你错误的输入或处理大的资源或其他的异常导致。我们来写一个实现,如果输入字节超出限制长度就抛出TooLongFrameException,这样的功能一般用来防止资源耗尽。看下图: |
| 5 | + |
| 6 | +在图10.4最大帧大小被设置为3个字节。 |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +Figure 10.4 Decoding via FrameChunkDecoder |
| 11 | + |
| 12 | +上图显示帧的大小被限制为3字节,若输入的字节超过3字节,则超过的字节被丢弃并抛出 TooLongFrameException。在 ChannelPipeline 中的其他ChannelHandler 实现可以处理 TooLongFrameException 或者忽略异常。处理异常在 ChannelHandler.exceptionCaught() 方法中完成,ChannelHandler 提供了一些具体的实现,看下面代码: |
| 13 | + |
| 14 | + public class FrameChunkDecoder extends ByteToMessageDecoder { //1 |
| 15 | + |
| 16 | + private final int maxFrameSize; |
| 17 | + |
| 18 | + public FrameChunkDecoder(int maxFrameSize) { |
| 19 | + this.maxFrameSize = maxFrameSize; |
| 20 | + } |
| 21 | + |
| 22 | + @Override |
| 23 | + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { |
| 24 | + int readableBytes = in.readableBytes(); //2 |
| 25 | + if (readableBytes > maxFrameSize) { |
| 26 | + // discard the bytes //3 |
| 27 | + in.clear(); |
| 28 | + throw new TooLongFrameException(); |
| 29 | + } |
| 30 | + ByteBuf buf = in.readBytes(readableBytes); //4 |
| 31 | + out.add(buf); //5 |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | +1. 继承 ByteToMessageDecoder 用于解码入站字节到消息 |
| 36 | +2. 指定最大需要的帧产生的体积 |
| 37 | +3. 如果帧太大就丢弃并抛出一个 TooLongFrameException 异常 |
| 38 | +4. 同时从 ByteBuf 读到新帧 |
| 39 | +5. 添加帧到解码消息 List |
| 40 | + |
| 41 | +示例如下: |
| 42 | + |
| 43 | +Listing 10.6 Testing FixedLengthFrameDecoder |
| 44 | + |
| 45 | + |
| 46 | + public class FrameChunkDecoderTest { |
| 47 | + |
| 48 | + @Test //1 |
| 49 | + public void testFramesDecoded() { |
| 50 | + ByteBuf buf = Unpooled.buffer(); //2 |
| 51 | + for (int i = 0; i < 9; i++) { |
| 52 | + buf.writeByte(i); |
| 53 | + } |
| 54 | + ByteBuf input = buf.duplicate(); |
| 55 | + |
| 56 | + EmbeddedChannel channel = new EmbeddedChannel(new FrameChunkDecoder(3)); //3 |
| 57 | + Assert.assertTrue(channel.writeInbound(input.readBytes(2))); //4 |
| 58 | + try { |
| 59 | + channel.writeInbound(input.readBytes(4)); //5 |
| 60 | + Assert.fail(); //6 |
| 61 | + } catch (TooLongFrameException e) { |
| 62 | + // expected |
| 63 | + } |
| 64 | + Assert.assertTrue(channel.writeInbound(input.readBytes(3))); //7 |
| 65 | + |
| 66 | + |
| 67 | + Assert.assertTrue(channel.finish()); //8 |
| 68 | + |
| 69 | + ByteBuf read = (ByteBuf) channel.readInbound(); |
| 70 | + Assert.assertEquals(buf.readSlice(2), read); //9 |
| 71 | + read.release(); |
| 72 | + |
| 73 | + read = (ByteBuf) channel.readInbound(); |
| 74 | + Assert.assertEquals(buf.skipBytes(4).readSlice(3), read); |
| 75 | + read.release(); |
| 76 | + |
| 77 | + buf.release(); |
| 78 | + } |
| 79 | + } |
| 80 | + |
| 81 | +1. 使用 @Test 注解 |
| 82 | +2. 新建 ByteBuf 写入 9 个字节 |
| 83 | +3. 新建 EmbeddedChannel 并安装一个 FixedLengthFrameDecoder 用于测试 |
| 84 | +4. 写入 2 个字节并预测生产的新帧(消息) |
| 85 | +5. 写一帧大于帧的最大容量 (3) 并检查一个 TooLongFrameException 异常 |
| 86 | +6. 如果异常没有被捕获,测试将失败。注意如果类实现 exceptionCaught() 并且处理了异常 exception,那么这里就不会捕捉异常 |
| 87 | +7. 写剩余的 2 个字节预测一个帧 |
| 88 | +8. 标记 channel 完成 |
| 89 | +9. 读到的产生的消息并且验证值。注意 assertEquals(Object,Object)测试使用 equals() 是否相当,不是对象的引用是否相当 |
| 90 | + |
| 91 | +即使我们使用 EmbeddedChannel 和 ByteToMessageDecoder。 |
| 92 | + |
| 93 | +应该指出的是,同样的可以做每个 ChannelHandler 的实现,将抛出一个异常。 |
| 94 | + |
| 95 | +乍一看,这看起来很类似于测试我们写在清单10.2中,但它有一个有趣的转折,即 TooLongFrameException 的处理。这里使用的 try/catch 块是 EmbeddedChannel 的一种特殊的特性。如果其中一个“write*"编写方法产生一个受控异常将被包装在一个 RuntimeException。这使得测试更加容易,如果异常处理的一部分处理。 |
| 96 | + |
0 commit comments