本篇博文是《从0到1学习 Netty》中 NIO 系列的第四篇博文,主要内容是介绍如何处理消息边界以及通过可写事件解决写入内容过多的问题,往期系列文章请访问博主的 Netty 专栏,博文中的所有代码全部收集在博主的 GitHub 仓库中;
消息边界将缓冲区的大小设置为4个字节,发送以下消息时:
sc.write(Charset.defaultCharset().encode("你好,sidiot!"));运行结果:
你�����sidiot���这是因为 UTF-8 字符集下,1个汉字占用3个字节,此时缓冲区大小为4个字节,一次读时间无法处理完通道中的所有数据,所以会触发多次读事件。这导致其他几个中文字符被拆分开来发送,因此解码时就会出现如上问题。
一般的解决思路有以下三种:
固定消息长度,数据包大小一样,服务器按预定长度读取,当发送的数据较少时,需要将数据进行填充,直到长度与消息规定长度一致,缺点是浪费带宽;按分隔符拆分,缺点是效率低,需要一个一个字符地去匹配分隔符;TLV 格式,即 Type 类型、Length 长度、Value 数据,也就是在消息开头用一些空间存放后面数据的长度,如 HTTP 请求头中的 Content-Type 与 Content-Length。类型和长度已知的情况下,就可以方便获取消息大小,分配合适的 buffer,缺点是 buffer 需要提前分配,如果内容过大,则影响 server 的吞吐量;Http 1.1 是 TLV 格式;Http 2.0 是 LTV 格式;接下来通过按分隔符拆分的方式来处理消息边界问题;
先编写一个 split() 函数,用于处理将 buffer 的内容按分隔符进行拆分,代码如下:
private static void split(ByteBuffer buffer) {buffer.flip();for(int i=0; i