模拟LTV消息格式实现NIO简单聊天功能
本篇我们根据上一篇(听说你不知道Java并发编程中三种I/O模型?看这一篇就够了)所写一个简单的聊天功能,主要是为了解决消息半包、粘包
等问题,聊天功能仅做简单实现。我们将NIO示例中读取消息的 ByteBuffer 长度修改为8,然后通过客户端给服务端发送一条消息,此时我们看一下日志:
可以看到,当我们发送你好
时,服务器正常接收,而后面消息出现了乱码,这是因为服务器每次使用固定长度8个字节
来接收我们的消息,而一个中文是3个字节
长度,导致部分汉字被拆分成乱码而无法识别。
解决该问题的方法可分为三种,分别是:
固长
:约定消息长度,每次发送和读取消息数据包使用的大小都一样,当消息自身长度少时会带宽浪费分隔符
:约定分隔符比如:\n
,每次读取到消息后挨个查看是否有分隔符,然后按照获取到的长度读取,此时每条消息都需要从前往后查找约定分隔符,效率低下,且如果消息自身就有约定的分隔符可能会导致消息不完整TLV格式
:即ype
类型、Length
长度、Vaule
数据,在长度和类型已知的情况下,可以快速获取消息大小,分配合适的 Buffer,缺点是 Buffer 需要提前分配,如果内容过大,可能会影响server吞吐量。如:Http1.1是TLV
格式;Http2.0是LTV
格式
接下来这里模拟TLV格式
来完成消息发送,当服务器收到客户端消息后直接将消息转发给其它客户端
发送消息逻辑
这里模拟TLV格式
,定义LENGTH_SIZE
用4个字节来表示消息的长度,定义BUFFER_SIZE
默认缓冲区大小为256
private static final int LENGTH_SIZE = 4;
private static final int BUFFER_SIZE = 256;
可以看到,我们发送的消息长度会使用消息长度+消息
作为真实消息内容进行发送。先将消息的长度写入 ByteBuffer 前 4 个字节中,即设置了长度字段的值;然后将消息的内容写入缓冲区,紧跟在长度字段后面。如果要定根据消息类型再加上一个类似定义即可,此处未做更多处理
服务端实现:
服务端代码改动不多,仅在读取消息时做一次消息转发操作,使用自定义消息发送逻辑将消息转发给除发送者外所有客户端
客户端实现:
客户端加上开启独立线程来读取服务器消息的代码,使用自定义消息发送逻辑发送消息即可
接收消息逻辑
这里首先对实际消息长度进行判断,如果长度大于现有缓冲区大小,则进行扩容操作,然后重新挂载附件,等待下一次消息读取事件;当实际消息长度较小且定义缓冲区大小过大时,将缓冲区进行缩容操作并重新挂载
日志
开启两个客户端模拟聊天场景,从日志可以看到我们定义的自动扩容和缩容操作生效
以上便是根据NIO实现的一个非常简单的聊天功能,主要用来演示处理消息的粘包问题的处理思路
- 本文标签: java io
- 本文链接: https://www.58cto.cn/article/51
- 版权声明: 本文由程序言原创发布, 非商业性可自由转载、引用,但需署名作者且注明文章出处:程序言 》 模拟LTV消息格式实现NIO简单聊天功能 - https://www.58cto.cn/article/51