Author: haoransun
Wechat: SHR—97
一、概述
官方网站:http://netty.io/
Netty是一款异步事件驱动的网络通信框架,用来快速开发可维护的高性能、高扩展的协议服务器和客户端。
Netty是一个基于NI0客户端、服务器框架,使用它可以轻松快速的的构建网络应用,如协议服务器和客户端。它极大地简化了TCP和UDP套接字服务器网络编程开发。
高性能、稳定、高度灵活,社区活跃,有大量成功的商业实践项目
二、Netty编程模型
Maven坐标
1
2
3
4
5<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
服务器
- 服务器编程步骤:
1.创建服务器引导对象
2.创建请求转发和IO处理的线程池组
3.绑定线程池
4.设置服务的实现类
5.初始化通讯管道(重点)
6.绑定端口启动服务
7.关闭服务
8.释放资源
- 服务器示例代码:
1 | /** |
- 服务器 通道处理类 示例代码:
1 | /** |
客户端
- 客户端编程步骤:
1.初始化客户端引导程序
2.创建IO处理线程池
3.设置客户端的实现类
4.绑定线程池
5.初始化客户端通道(重点)
6.连接服务器
7.关闭通道
8.释放资源
- 客户端示例代码:
1 | /** |
- 客户端 通道处理类 示例代码:
1 | /** |
三、异常处理
模拟问题
1.修改 NettyserverChannelHandlerAdapter 第44行成 ctx.writeandFlush(new Date())
2.启动Server、Client测试,测试发现客户端无法获取服务器响应
Netty默认情况下,只支持ByteBuf类型传输,不支持对象类型传输,直接写入对象会产生异常,通过添加istener处理异常
添加监听器
1 | ChannelFuture channelFuture = ctx.writeAndFlush(new Date()); |
四、编解码
在异常处理中,我们发现Netty默认采用ByteBuf
传输数据,如果我们需要传输对象类型,需要自定义Channel,添加编解码器,以支持对象类型数据传输。
导入Maven依赖
1 | <dependencies> |
定制解码器
1 | /** |
定制解码器
1 | /** |
组装通道处理器
1 | //5. 初始化通讯管道(服务器) |
修改服务器和客户端事件回调方法(读写直接操作对象)
1 | /** |
五、TCP粘包/拆包问题
模拟问题
- 继续修改NettyServerChannelHandlerAdapter,尝试给客户端响应多个对象,如图所示:
- 测试发现客户端解码过程中产生异常或者出现丢失数据情况
问题原因
TCP是个“流”协议,所谓流,就是没有分界线的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上,一个完整的数据包可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP拆包和粘包问题。
现在假设客户端向服务端连续发送了两个数据包,用packet1和packet2来表示,那么服务端收到的数据可以分为三种,现列举如下:
第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象
第二种情况,接收端只收到一个数据包,由于TCP是不会出现丢包的,所以这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理
第三种情况,这种情况有两种表现形式,如下图。接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。
粘包和拆包问题的解决策略
由于底层的TCP无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,所以只能通过上层的应用协议栈设计解决,常用的解决策略如下:
- 消息定长,例如每个报文的大小固定长度200字节,如果不够,空位补空格
- 在包尾增加回车换行符进行分割,例如FTP协议
- 将消息分为消息头和消息体,消息头中记录了消息体的长度
- 更复杂的应用层协议
使用Netty的编解码器解决粘包和拆包原理
o.netty.handler.codec.LengthFieldPrepender
编码器:负责在待发送的ByteBuf消息头上增加一个长度字段,用来标识消息的长度*
io.nety.handler.codec.LengthFieldBasedFrameDecoder
解码器:基于消息长度的半包的解码器*
- 通讯管道配置
代码实现
- Client
- Server
- 测试
Netty原理示意图
简单的自定义RPC设计