写一个 Netty 玩具
基本步骤


案例与数据结构


这里 Message Body 使用 JSON 格式,当然也能用其他的格式。
编码
- 首先要解决粘包、半包问题。
- 接着二次解码,处理各种格式的包内容。
- 然后是具体的 handler 方法编写。
- 然后是服务器端、客户端内容。值得注意的是对于服务端的逻辑流程与客户端相反,只要注意 request 和 response 的填写即可。
MessageToMessageEncoder中会自动匹配类型,类型不符合会在 pipline 中跳过。
完善客户端
请求与请求之间不应该有联系:

为了达到上图的目的,我们需要有 id-future 的关系,在 request 里 new 一个 future ,保存关系,然后用 future.get() 来等待结果,结果带有相同的 id 。
思考
LengthFieldBasedFrameDecoder中initialBytesToStrip未考虑设置;- 如果不设置,结果含有
length字段,会解析失败。 - 默认该值为 0 ,但是不一定正确。
- 如果不设置,结果含有
ChannelHandler顺序不正确,ChannelHandler该共享不共享,不该共享却共享;pipline里的顺序非常重要,先解码,再处理,再编码,注意编码穿插在解码后。
- 分配
ByteBuf:分配器直接用ByteBufAllocator.DEFAULT等,而不是采用ChannelHandlerContext.alloc();- 大多数情况没有问题,
ctx.alloc()是可以切换堆外、堆内等情况的,如果换了实现,ByteBufAllocator.DEFAULT就不支持了。
- 大多数情况没有问题,
- 未考虑
ByteBuf的释放;- 在 Handler 中使用
SimpleChannelInboundHandler的好处就是有内置的释放ByteBuf,即release()方法。
- 在 Handler 中使用
- 错以为
ChannelHandlerContext.write(msg)就写出数据了;write仅仅是加到队列而不是发出去。
- 乱用
ChannelHandlerContext.channel().writeAndFlush(msg);- 还有一种
ctx.channel().writeAndFlush(msg)是完整的 pipline 重新走一遍(客户端可能会用),而ctx.writeAndFlush()仅仅是在当前 pipline 的位置寻找下一个可以走的位置(服务端常用)。
- 还有一种