编码器和解码器完成,但仍有一些缺失:测试。
没有测试你只看到如果编解码器工作对一些真正的服务器运行时,这并不是你应该是依靠什么。第十章所示,为一个自定义编写测试 ChannelHandler通常是通过 EmbeddedChannel。
所以这正是现在做测试我们定制的编解码器,其中包括一个编码器和解码器。让重新开始编码器。后面的清单显示了简单的编写单元测试。
Listing 14.5 MemcachedRequestEncoderTest class
public class MemcachedRequestEncoderTest {
~~~
@Test
public void testMemcachedRequestEncoder() {
MemcachedRequest request = new MemcachedRequest(Opcode.SET, "key1", "value1"); //1
EmbeddedChannel channel = new EmbeddedChannel(new MemcachedRequestEncoder()); //2
channel.writeOutbound(request); //3
ByteBuf encoded = (ByteBuf) channel.readOutbound();
Assert.assertNotNull(encoded); //4
Assert.assertEquals(request.magic(), encoded.readUnsignedByte()); //5
Assert.assertEquals(request.opCode(), encoded.readByte()); //6
Assert.assertEquals(4, encoded.readShort());//7
Assert.assertEquals((byte) 0x08, encoded.readByte()); //8
Assert.assertEquals((byte) 0, encoded.readByte());//9
Assert.assertEquals(0, encoded.readShort());//10
Assert.assertEquals(4 + 6 + 8, encoded.readInt());//11
Assert.assertEquals(request.id(), encoded.readInt());//12
Assert.assertEquals(request.cas(), encoded.readLong());//13
Assert.assertEquals(request.flags(), encoded.readInt()); //14
Assert.assertEquals(request.expires(), encoded.readInt()); //15
byte[] data = new byte[encoded.readableBytes()]; //16
encoded.readBytes(data);
Assert.assertArrayEquals((request.key() + request.body()).getBytes(CharsetUtil.UTF_8), data);
Assert.assertFalse(encoded.isReadable()); //17
Assert.assertFalse(channel.finish());
Assert.assertNull(channel.readInbound());
}
~~~
}
1. 新建 MemcachedRequest 用于编码为 ByteBuf
2. 新建 EmbeddedChannel 用于保持 MemcachedRequestEncoder 到测试
3. 写请求到 channel 并且判断是否产生了编码的消息
4. 检查 ByteBuf 是否 null
5. 判断 magic 是否正确写入 ByteBuf
6. 判断 opCode (SET) 是否写入正确
7. 检查 key 是否写入长度正确
8. 检查写入的请求是否额外包含
9. 检查数据类型是否写
10. 检查是否保留数据插入
11. 检查 body 的整体大小 计算方式是 key.length + body.length + extras
12. 检查是否正确写入 id
13. 检查是否正确写入 Compare and Swap (CAS)
14. 检查是否正确的 flag
15. 检查是否正确设置到期时间的
16. 检查 key 和 body 是否正确
17. 检查是否可读
Listing 14.6 MemcachedResponseDecoderTest class
~~~
public class MemcachedResponseDecoderTest {
@Test
public void testMemcachedResponseDecoder() {
EmbeddedChannel channel = new EmbeddedChannel(new MemcachedResponseDecoder()); //1
byte magic = 1;
byte opCode = Opcode.SET;
byte[] key = "Key1".getBytes(CharsetUtil.US_ASCII);
byte[] body = "Value".getBytes(CharsetUtil.US_ASCII);
int id = (int) System.currentTimeMillis();
long cas = System.currentTimeMillis();
ByteBuf buffer = Unpooled.buffer(); //2
buffer.writeByte(magic);
buffer.writeByte(opCode);
buffer.writeShort(key.length);
buffer.writeByte(0);
buffer.writeByte(0);
buffer.writeShort(Status.KEY_EXISTS);
buffer.writeInt(body.length + key.length);
buffer.writeInt(id);
buffer.writeLong(cas);
buffer.writeBytes(key);
buffer.writeBytes(body);
Assert.assertTrue(channel.writeInbound(buffer)); //3
MemcachedResponse response = (MemcachedResponse) channel.readInbound();
assertResponse(response, magic, opCode, Status.KEY_EXISTS, 0, 0, id, cas, key, body);//4
}
@Test
public void testMemcachedResponseDecoderFragments() {
EmbeddedChannel channel = new EmbeddedChannel(new MemcachedResponseDecoder()); //5
byte magic = 1;
byte opCode = Opcode.SET;
byte[] key = "Key1".getBytes(CharsetUtil.US_ASCII);
byte[] body = "Value".getBytes(CharsetUtil.US_ASCII);
int id = (int) System.currentTimeMillis();
long cas = System.currentTimeMillis();
ByteBuf buffer = Unpooled.buffer(); //6
buffer.writeByte(magic);
buffer.writeByte(opCode);
buffer.writeShort(key.length);
buffer.writeByte(0);
buffer.writeByte(0);
buffer.writeShort(Status.KEY_EXISTS);
buffer.writeInt(body.length + key.length);
buffer.writeInt(id);
buffer.writeLong(cas);
buffer.writeBytes(key);
buffer.writeBytes(body);
ByteBuf fragment1 = buffer.readBytes(8); //7
ByteBuf fragment2 = buffer.readBytes(24);
ByteBuf fragment3 = buffer;
Assert.assertFalse(channel.writeInbound(fragment1)); //8
Assert.assertFalse(channel.writeInbound(fragment2)); //9
Assert.assertTrue(channel.writeInbound(fragment3)); //10
MemcachedResponse response = (MemcachedResponse) channel.readInbound();
assertResponse(response, magic, opCode, Status.KEY_EXISTS, 0, 0, id, cas, key, body);//11
}
private static void assertResponse(MemcachedResponse response, byte magic, byte opCode, short status, int expires, int flags, int id, long cas, byte[] key, byte[] body) {
Assert.assertEquals(magic, response.magic());
Assert.assertArrayEquals(key, response.key().getBytes(CharsetUtil.US_ASCII));
Assert.assertEquals(opCode, response.opCode());
Assert.assertEquals(status, response.status());
Assert.assertEquals(cas, response.cas());
Assert.assertEquals(expires, response.expires());
Assert.assertEquals(flags, response.flags());
Assert.assertArrayEquals(body, response.data().getBytes(CharsetUtil.US_ASCII));
Assert.assertEquals(id, response.id());
}
}
~~~
1. 新建 EmbeddedChannel ,持有 MemcachedResponseDecoder 到测试
2. 创建一个新的 Buffer 并写入数据,与二进制协议的结构相匹配
3. 写缓冲区到 EmbeddedChannel 和检查是否一个新的MemcachedResponse 创建由声明返回值
4. 判断 MemcachedResponse 和预期的值
5. 创建一个新的 EmbeddedChannel 持有 MemcachedResponseDecoder 到测试
6. 创建一个新的 Buffer 和写入数据的二进制协议的结构相匹配
7. 缓冲分割成三个片段
8. 写的第一个片段 EmbeddedChannel 并检查,没有新的MemcachedResponse 创建,因为并不是所有的数据都是准备好了
9. 写第二个片段 EmbeddedChannel 和检查,没有新的MemcachedResponse 创建,因为并不是所有的数据都是准备好了
10. 写最后一段到 EmbeddedChannel 和检查新的 MemcachedResponse 是否创建,因为我们终于收到所有数据
11. 判断 MemcachedResponse 与预期的值
- Introduction
- 开始
- Netty-异步和数据驱动
- Netty 介绍
- 构成部分
- 关于本书
- 第一个 Netty 应用
- 设置开发环境
- Netty 客户端/服务端 总览
- 写一个 echo 服务器
- 写一个 echo 客户端
- 编译和运行 Echo 服务器和客户端
- 总结
- Netty 总览
- Netty 快速入门
- Channel, Event 和 I/O
- 什么是 Bootstrapping 为什么要用
- ChannelHandler 和 ChannelPipeline
- 近距离观察 ChannelHandler
- 总结
- 核心功能
- Transport(传输)
- 案例研究:Transport 的迁移
- Transport API
- 包含的 Transport
- Transport 使用情况
- 总结
- Buffer(缓冲)
- Buffer API
- ByteBuf - 字节数据的容器
- 字节级别的操作
- ByteBufHolder
- ByteBuf 分配
- 总结
- ChannelHandler 和 ChannelPipeline
- ChannelHandler 家族
- ChannelPipeline
- ChannelHandlerContext
- 总结
- Codec 框架
- 什么是 Codec
- Decoder(解码器)
- Encoder(编码器)
- 抽象 Codec(编解码器)类
- 总结
- 提供了的 ChannelHandler 和 Codec
- 使用 SSL/TLS 加密 Netty 程序
- 构建 Netty HTTP/HTTPS 应用
- 空闲连接以及超时
- 解码分隔符和基于长度的协议
- 编写大型数据
- 序列化数据
- 总结
- Bootstrap 类型
- 引导客户端和无连接协议
- 引导服务器
- 从 Channel 引导客户端
- 在一个引导中添加多个 ChannelHandler
- 使用Netty 的 ChannelOption 和属性
- 关闭之前已经引导的客户端或服务器
- 总结
- 引导
- Bootstrap 类型
- 引导客户端和无连接协议
- 引导服务器
- 从 Channel 引导客户端
- 在一个引导中添加多个 ChannelHandler
- 使用Netty 的 ChannelOption 和属性
- 关闭之前已经引导的客户端或服务器
- 总结
- NETTY BY EXAMPLE
- 单元测试
- 总览
- 测试 ChannelHandler
- 测试异常处理
- 总结
- WebSocket
- WebSocket 程序示例
- 添加 WebSocket 支持
- 测试程序
- 总结
- SPDY
- SPDY 背景
- 示例程序
- 实现
- 启动 SpdyServer 并测试
- 总结
- 通过 UDP 广播事件
- UDP 基础
- UDP 广播
- UDP 示例
- EventLog 的 POJO
- 写广播器
- 写监视器
- 运行 LogEventBroadcaster 和 LogEventMonitor
- 总结
- 高级主题
- 实现自定义的编解码器
- 编解码器的范围
- 实现 Memcached 编解码器
- 了解 Memcached 二进制协议
- Netty 编码器和解码器
- 测试编解码器
- EventLoop 和线程模型
- 线程模型的总览
- EventLoop
- EventLoop
- I/O EventLoop/Thread 分配细节
- 总结
- 用例1:Droplr Firebase 和 Urban Airship
- 用例2:Facebook 和 Twitter