Socket粘包问题终极解决方案—Netty版(2W字)!(1)

简介: Socket粘包问题终极解决方案—Netty版(2W字)!(1)

上一篇我们写了《Socket粘包问题的3种解决方案》,但没想到评论区竟然炸了。介于大家的热情讨论,以及不同的反馈意见,本文就来做一个扩展和延伸,试图找到问题的最优解,以及消息通讯的最优解决方案。


在正式开始之前,我们先对上篇评论中的几个典型问题做一个简单的回复,不感兴趣的朋友可直接划过。


问题一:TCP存在粘包问题吗?


先说答案:TCP 本身并没有粘包和半包一说,因为 TCP 本质上只是一个传输控制协议(Transmission Control Protocol,TCP),它是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。


所谓的协议本质上是一个约定,就好比 Java 编程约定使用驼峰命名法一样,约定的意义是为了让通讯双方,能够正常的进行消息互换的,那粘包和半包问题又是如何产生的呢?

这是因为在 TCP 的交互中,数据是以字节流的形式进行传输的,而“流”的传输是没有边界的,因为没有边界所以就不能区分消息的归属,从而就会产生粘包和半包问题(粘包和半包的定义,详见上一篇)。所以说 TCP 协议本身并不存在粘包和半包问题,只是在使用中如果不能有效的确定流的边界就会产生粘包和半包问题。


问题二:分隔符是最优解决方案?


坦白的说,经过评论区大家的耐心“开导”,我也意识到了以结束符作为最终的解决方案存在一定的局限性,比如当一条消息中间如果出现了结束符就会造成半包的问题,所以如果是复杂的字符串要对内容进行编码和解码处理,这样才能保证结束符的正确性。


问题三:Socket 高效吗?


这个问题的答案是否定的,其实上文在开头已经描述了应用场景:「传统的 Socket 编程」,学习它的意义就在于理解更早期更底层的一些知识,当然作为补充本文会提供更加高效的消息通讯方案——Netty 通讯。


聊完了以上问题,接下来咱们先来补充一下上篇文章中提到的,将消息分为消息头和消息体的代码实现。


一、封装消息头和消息体


在开始写服务器端和客户端之前,咱们先来编写一个消息的封装类,使用它可以将消息封装成消息头和消息体,如下图所示:


微信图片_20220120154257.jpg


消息头中存储消息体的长度,从而确定了消息的边界,便解决粘包和半包问题。


1.消息封装类


消息的封装类中提供了两个方法:一个是将消息转换成消息头 + 消息体的方法,另一个是读取消息头的方法,具体实现代码如下:


/**
 * 消息封装类
 */
class SocketPacket {
    // 消息头存储的长度(占 8 字节)
    static final int HEAD_SIZE = 8;
    /**
     * 将协议封装为:协议头 + 协议体
     * @param context 消息体(String 类型)
     * @return byte[]
     */
    public byte[] toBytes(String context) {
        // 协议体 byte 数组
        byte[] bodyByte = context.getBytes();
        int bodyByteLength = bodyByte.length;
        // 最终封装对象
        byte[] result = new byte[HEAD_SIZE + bodyByteLength];
        // 借助 NumberFormat 将 int 转换为 byte[]
        NumberFormat numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setMinimumIntegerDigits(HEAD_SIZE);
        numberFormat.setGroupingUsed(false);
        // 协议头 byte 数组
        byte[] headByte = numberFormat.format(bodyByteLength).getBytes();
        // 封装协议头
        System.arraycopy(headByte, 0, result, 0, HEAD_SIZE);
        // 封装协议体
        System.arraycopy(bodyByte, 0, result, HEAD_SIZE, bodyByteLength);
        return result;
    }
    /**
     * 获取消息头的内容(也就是消息体的长度)
     * @param inputStream
     * @return
     */
    public int getHeader(InputStream inputStream) throws IOException {
        int result = 0;
        byte[] bytes = new byte[HEAD_SIZE];
        inputStream.read(bytes, 0, HEAD_SIZE);
        // 得到消息体的字节长度
        result = Integer.valueOf(new String(bytes));
        return result;
    }
}



相关文章
|
5月前
|
网络协议
【Netty 网络通信】Socket 通信原理
【1月更文挑战第9天】【Netty 网络通信】Socket 通信原理
|
5月前
|
编解码 缓存 移动开发
TCP粘包/拆包与Netty解决方案
TCP粘包/拆包与Netty解决方案
88 0
|
2月前
|
编解码 网络协议 开发者
Netty运行原理问题之NettyTCP的粘包和拆包的问题如何解决
Netty运行原理问题之NettyTCP的粘包和拆包的问题如何解决
|
6天前
Netty 与硬件设备交互,下行命令时(服务对设备),如何等待设备响应,再进行业务操作解决方案
Netty 与硬件设备交互,下行命令时(服务对设备),如何等待设备响应,再进行业务操作解决方案
|
2月前
|
移动开发 网络协议 算法
(十)Netty进阶篇:漫谈网络粘包、半包问题、解码器与长连接、心跳机制实战
在前面关于《Netty入门篇》的文章中,咱们已经初步对Netty这个著名的网络框架有了认知,本章的目的则是承接上文,再对Netty中的一些进阶知识进行阐述,毕竟前面的内容中,仅阐述了一些Netty的核心组件,想要真正掌握Netty框架,对于它我们应该具备更为全面的认知。
|
4月前
|
网络协议
netty粘包问题分析
netty粘包问题分析
33 0
|
4月前
|
消息中间件 存储 网络协议
拼多多面试:Netty如何解决粘包问题?
粘包和拆包问题也叫做粘包和半包问题,**它是指在数据传输时,接收方未能正常读取到一条完整数据的情况(只读取了部分数据,或多读取到了另一条数据的情况)就叫做粘包或拆包问题。** 从严格意义上来说,粘包问题和拆包问题属于两个不同的问题,接下来我们分别来看。 ## 1.粘包问题 粘包问题是指在网络通信中,发送方连续发送的多个小数据包被接收方一次性接收的现象。这可能是因为底层传输层协议(如 TCP)会将多个小数据包合并成一个大的数据块进行传输,导致接收方在接收数据时一次性接收了多个数据包,造成粘连。 例如以下案例,正常情况下客户端发送了两条消息,分别为“ABC”和“DEF”,那么接收端也应该收到两
29 0
拼多多面试:Netty如何解决粘包问题?
|
4月前
|
Java
Netty传输object并解决粘包拆包问题
Netty传输object并解决粘包拆包问题
37 0
|
4月前
|
Java
Netty中粘包拆包问题解决探讨
Netty中粘包拆包问题解决探讨
23 0
|
5月前
|
监控 网络协议 iOS开发
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
程序退到后台的时候,所有线程被挂起,系统回收所有的socket资源问题及解决方案
95 0