Netty-13-EmbeddedChannel 测试 ChannelHandler
在这一节中,我们将讲解如何使用EmbeddedChannel 来测试ChannelHandler。
Junit 断言
org.junit.Assert 类提供了很多用于测试的静态方法。
失败的断言将导致一个异常被抛出,并将终止当前正在执行中的测试。
导入这些断言的最高效的方式是通过一个import static 语句来实现:
import static org.junit.Assert.*;
一旦这样做了,就可以直接调用Assert 方法了:
assertEquals(buf.readSlice(3), read);
解码器与编码器
解码器
Netty 提供了丰富的解码器抽象基类,我们可以很容易的实现这些基类来自定义解码器。主要分两类:
解码字节到消息(ByteToMessageDecoder 和 ReplayingDecoder)
解码消息到消息(MessageToMessageDecoder)
decoder 负责将“入站”数据从一种格式转换到另一种格式,Netty的解码器是一种 ChannelInboundHandler 的抽象实现。
实践中使用解码器很简单,就是将入站数据转换格式后传递到 ChannelPipeline 中的下一个ChannelInboundHandler 进行处理;这样的处理是很灵活的,我们可以将解码器放在 ChannelPipeline 中,重用逻辑。
编码器
就像decoder一样,Netty 也为你提供了一组类来写 encoder ,当然这些类提供的是与 decoder 完全相反的方法,如下所示:
编码从消息到字节
编码从消息到消息
测试入站消息
场景
我们先来编写一个简单的 ByteToMessageDecoder 实现,在有足够的数据可以读取的情况下将产生固定大小的包,如果没有足够的数据可以读取,则会等待下一个数据块并再次检查是否可以产生一个完整包。
如图所示,它可能会占用一个以上的“event”以获取足够的字节产生一个数据包,并将它传递到 ChannelPipeline 中的下一个 ChannelHandler,

正如可以从图9-2 右侧的帧看到的那样,这个特定的解码器将产生固定为 3 字节大小的帧。
因此,它可能会需要多个事件来提供足够的字节数以产生一个帧。
最终,每个帧都会被传递给 ChannelPipeline 中的下一个ChannelHandler。
该解码器的实现,如代码清单9-1 所示。
编码
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* @author binbin.hou
* @date 2019/5/1
* @since 0.0.1
*/
public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
/**
* 指定帧的长度
*/
private final int length;
public FixedLengthFrameDecoder(int length) {
this.length = length;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
// 当可读取的字节数大于帧指定的长度,则一直读取,并将内容添加到解码的消息列表中。
while (in.readableBytes() >= length) {
ByteBuf byteBuf = in.readBytes(length);
out.add(byteBuf);
}
}
}
单元测试
为了验证我们代码的正确性,我们来编写一个测试用例,测试下我们的代码。
正如我们前面所指出的,即使是在简单的代码中,单元测试也能帮助我们防止在将来代码重构时可能会导致的问题,并且能在问题发生时帮助我们诊断它们。
package com.github.houbb.netty.inaction.chap09;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import org.junit.Assert;
import org.junit.Test;
/**
* @author binbin.hou
* @date 2019/5/1
* @since 0.0.1
*/
public class FixedLengthFrameDecoderTest {
@Test
public void inTestOne() {
// 指定创建一个大小为9的 buffer
ByteBuf byteBuf = Unpooled.buffer();
for(int i = 0; i {
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception {
// 一个整形是4个字节
while(msg.readableBytes() >= 4) {
int value = msg.readInt();
//将绝对值设置进入编码信息列表
out.add(Math.abs(value));
}
}
}
测试代码
注意:此处写入的时候为 writeInt()
和编码器一一对应。
package com.github.houbb.netty.inaction.chap09;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import org.junit.Assert;
import org.junit.Test;
/**
* @author binbin.hou
* @date 2019/5/1
* @since 0.0.1
*/
public class AbsIntegerEncoderTest {
@Test
public void outTest() {
//1. 初始化 buffer
ByteBuf byteBuf = Unpooled.buffer();
for(int i = 1; i
* Also be aware that this method will NOT call {@link #retain()} and so the
* reference count will NOT be increased.
*
* @param length the size of the new slice
*
* @return the newly created slice
*
* @throws IndexOutOfBoundsException
* if {@code length} is greater than {@code this.readableBytes}
*/
public abstract ByteBuf readSlice(int length);
channel
- readInBound()
Return received data from this {@link Channel}