在使用Netty进行通信开发,如何选择编码器?在TCP粘包/拆包的问题如何解决?服务端在启动 流程是什么样的?连接服务流程是什么?
一 编解码器
1.1 什么叫编解码器
在网络传输的过程中,数据都是以字节流的方式进行传递。客户端在进行数据传递的时候 将原来的数据格式转化为字节,叫编码。服务端将字节转化为原来的格式,叫解码。统称 codec。 编解码器分为两部分-编码器和解码器,编码器负责出站,解码器负责入站。
1.2 解码器
1.2.1 概述
解码器负责入站操作,那么也一定要实现ChannelInboundHandler接口,所以解码器本质 上也是ChannelHandler。我们自定义编解码器只需要继承ByteToMessageDecoder(Netty提供抽象类,继承 ChannelInboundHandlerAdapter),实现decode()。Netty提供一些常用的解码器实现, 开箱即用。如下:
Netty也提供了MessageToMessageDecoder,将⼀种格式转化为另⼀种格式的解码器,也提供了⼀些 实现,如下:
1.2.2 将字节流转化为Intger类型(案例)
1 字节解码器
2 Handler
3 在pipeline中添加解码器
1.3 编码器
1.3.1 概述
将原来的格式转化为字节。我们要实现自定义解码器只要继承MessageToByteEncoder (实现了ChannelOutboundHandler接⼝),本质上也是ChannelHandler。 Netty中一些实现的编码器,如下:
Netty也提供了MessageToMessageEncoder,将⼀种格式转化为另⼀种格式的编码器,也提供了⼀些 实现:
1.3.2 将Integer类型编码为字节进⾏传递(案例)
- 自定义编码器
- Handler
- pipeline
二 开发Http服务器
通过Netty中提供的http的解码器,进行http服务器开发。
2.1 Netty配置
- server
- ServerHandler
- RequestParser
- 对象
2.2 服务端
- NettyObjectServer
- ServerHandler
2.3 客户端
- NettyObjectClient
- ClientHandler
2.4 JDK序列化的优化
JDK序列化使⽤是⽐较⽅便,但是性能较差,序列化后的字节⽐较⼤,所以⼀般在项⽬中不 会使⽤⾃带的序列化,⽽是会采⽤第三⽅的序列化框架Hessian编解码。
- 导入依赖
- User对象
- Hessian序列化⼯具类
- 编码器
- 解码器
- 服务端
- 客户端
二 TCP的粘包/拆包的问题以及解决
2.1 ReplayingDecoder
- 自定义解码器,将buf变为int
2. 使用ReplayingDecoder进行优化
- ReplayingDecoder使用说明
- 注意
继承ReplayingDecoder,错误示例和修改
ByteToIntegerDecoder2的实现
2.2 拆包和粘包问题重现(客户端向服务端发送十条数据)
- 客户端启动类
- 客户端ClientHandler
- 服务端NettyServer
- ServerHandler
2.2 什么是TCP的粘包和拆包问题
TCP是流传递的,所谓流,就是没有界限的数据。服务端接受客户端数据,并不知道是一条还是多条。 服务端如何拆包并不知道。
因此服务端和客户端进行数据传递的时候,要制定好拆包规则。客户端按照该规则进行粘包,服务端 按照该规则拆包。如果有任意违背该规则,服务端就不能拿到预期的数据。
- 解决思路(三种)
2.3 实战:解决TCP的粘包/拆包问题
- 自定义协议
- 编码器
- 解码器
- 客户端ClientHandler
- NettyClient
- ServerHandler
- NettyServer
- 测试
三 Netty核心源码解析
3.1 服务端启动过程刨析
- 创建服务端Channel
- 初始化服务端Channel
- 注册selector
- 绑定端口
什么时候进⾏更新selector的主从事件?最终在io.netty.channel.nio.AbstractNioChannel#doBeginRead()⽅法中完成的
3.2 连接请求过程源码刨析
- 新连接接入
- 注册读事件
四 使用Netty优化点
4.1 零拷贝
4.2 EventLoop的任务调度
而不是使用hannel.writeAndFlush(data);EventLoop的任务调度直接放入到channel所对应的EventLoop的执行队列,后者会导致线程切换。备注:在writeAndFlush的底层,如果没有通过eventLoop执行的话,就会启动新的线程。
4.3 减少ChannelPipline的调⽤⻓度
4.4 减少ChannelHandler的创建(基本上不会配置)
如果channelhandler是⽆状态的(即不需要保存任何状态参数),那么使⽤Sharable注解,并在 bootstrap时只创建⼀个实例,减少GC。否则每次连接都会new出handler对象。
注意:ByteToMessageDecoder之类的编解码器是有状态的,不能使⽤Sharable注解。
4.5 配置参数的设置
五 ByteBuf的api
- 顺序读api
- 顺序写操作
更多Java相关内容。请关注微信公众号 花花与Java