TCP粘包和拆包问题及其解决方法

这篇具有很好参考价值的文章主要介绍了TCP粘包和拆包问题及其解决方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

含义:

TCP 传输协议是面向流的,没有数据包界限,也就是说消息无边界。客户端向服务端发送数据时,可能将一个完整的报文拆分成多个小报文进行发送,也可能将多个报文合并成一个大的报文进行发送。(TCP协议的底层,并不了解上层业务的具体定义,它会根据TCP缓冲区的实际情况进行包的划分。在业务层面认为一个完整的包,可能会被TCP拆分成多个小包进行发送,也可能把多个小的包封装成一个大的数据包进行发送,这就是所谓的TCP粘包拆包问题。)。
因此就有了拆包和粘包。 在网络通信的过程中,每次可以发送的数据包大小是受多种因素限制的,如 MTU 传输单元大小、滑动窗口等。
所以如果一次传输的网络包数据大小超过传输单元大小,那么我们的数据可能会拆分为多个数据包发送出去。 如果每次请求的网络包数据都很小,比如一共请求了 10000 次,TCP 并不会分别发送 10000 次。 TCP采用的 Nagle(批量发送,主要用于解决频繁发送小数据包而带来的网络拥塞问题) 算法对此作出了优化。

TCP粘包和拆包问题及其解决方法

客户端发送了两个数据包P1和P2给服务端,服务端一次读取到的字节数是不确定的,可能存在以下4种情况:

(1)服务端分两次读取到了两个独立的数据包P1和P2,没有发送粘包和拆包;
(2)服务端一次读到

了两个数据包,P1和P2粘在一起,这就是TCP粘包情况;
(3)服务端分两次读取到了两个数据包,第一次读取了完整的P1包和P2包的一部分,第二次读取到了P2包的剩余部分,这被称为TCP拆包;
(4)服务端分两次读取了两个数据包,第一次读取了P1包的一部分,第二次读取到了P1包的剩余部分,这也是TCP拆包;

解决方法:

由于TCP协议底层无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,所以,这个问题只能通过上层的应用层协议设计来解决,常见方案如下:

(1)消息定长,发送方和接收方规定固定大小的消息长度,例如每个报文大小固定为200字节,如果不够,空位补空格;(消息定长法使用非常简单,但是缺点也非常明显,无法很好设定固定长度的值,如果长度太大会造成字节浪费,长度太小又会影响消息传输,所以在一般情况下消息定长法不会被采用。)
(2)在包围增加特殊字符进行分割,例如FTP协议;分隔符的选择一定要避免和消息体中字符相同,以免冲突。否则可能出现错误的消息拆分。比较推荐的做法是将消息进行编码,例如 base64 编码,然后可以选择 64 个编码字符之外的字符作为特定分隔符。
(3)自定义协议,将消息分为消息头和消息体,消息头中包含消息总长度,这样服务端就可以知道每个数据包的具体长度了,知道了发送数据包的具体边界后,就可以解决粘包和拆包问题了;

TCP粘包和拆包问题及其解决方法

netty解决粘包拆包问题:

DelimiterBasedFrameDecoder:每个应用层数据包,都通过自定义分隔符,进行分割拆分
LineBasedFrameDecoder:每个应用层数据包,都以换行符作为分隔符,进行分割拆分。
FixedLengthFrameDecoder:每个应用层数据包的拆分都是固定长度大小
LengthFieldBasedFrameDecoder+LengthFieldPrepender:自定义消息长度。 将应用层数据包的长度,作为接收端应用层数据包的拆分依据。按照应用层数据包的大小,进行拆包。这个拆包器有个要求,应用层协议包含数据包长度。(LengthFieldPrepender:待发送消息长度写入到前几个字节)。

笔者本人自研rpc框架的编解码自定义协议:

先读取消息类型(Requst, Response), 序列化方式(原生, json 加上消息长度:防止粘包, 再根据长度读取data.

消息类型(2Byte) 序列化方式 2Byte 消息长度 4Byte
序列化后的Data…. 序列化后的Data… 序列化后的Data….
编码 
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
        // 写入消息类型
        if(msg instanceof RPCRequest){
            out.writeShort(MessageType.REQUEST.getCode());
        }
        else if(msg instanceof RPCResponse){
            out.writeShort(MessageType.RESPONSE.getCode());
        }
        // 写入序列化方式
        out.writeShort(serializer.getType());
        // 得到序列化数组
        byte[] serialize = serializer.serialize(msg);
        // 写入长度
        out.writeInt(serialize.length);
        // 写入序列化字节数组
        out.writeBytes(serialize);
    }
解码
 protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // 1. 读取消息类型
        short messageType = in.readShort();
        // 现在还只支持request与response请求
        if(messageType != MessageType.REQUEST.getCode() &&
                messageType != MessageType.RESPONSE.getCode()){
            System.out.println("暂不支持此种数据");
            return;
        }
        // 2. 读取序列化的类型
        short serializerType = in.readShort();
        // 根据类型得到相应的序列化器
        Serializer serializer = Serializer.getSerializerByCode(serializerType);
        if(serializer == null)throw new RuntimeException("不存在对应的序列化器");
        // 3. 读取数据序列化后的字节长度
        int length = in.readInt();
        // 4. 读取序列化数组
        byte[] bytes = new byte[length];
        in.readBytes(bytes);
        // 用对应的序列化器解码字节数组
        Object deserialize = serializer.deserialize(bytes, messageType);
        out.add(deserialize);
    }

 文章来源地址https://www.toymoban.com/news/detail-420710.html

到了这里,关于TCP粘包和拆包问题及其解决方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 「 计算机网络 」TCP的粘包拆包问题

    参考鸣谢 大病初愈,一分钟看懂TCP粘包拆包 雷小帅 TCP 的粘包拆包以及解决方案 一乐说 当我们在进行网络传输时,由于各种原因,数据包的发送和接收可能会出现粘包和拆包的问题。粘包和拆包都是数据分组错误的情况,其中粘包指的是多个数据包被合并成一个,而拆包则

    2024年02月01日
    浏览(35)
  • 【计网】一起聊聊TCP的粘包拆包问题吧

    在TCP中,粘包和拆包问题是十分常见的,如 基于TCP协议 的RPC框架、Netty等。 粘包(Packet Stickiness) 指的是在网络通信中,发送方连续发送的多个小数据包被接收方一次性接收的现象。这可能是因为底层传输层协议(如TCP)会将 多个小数据包合并成一个大的数据块 进行传输,导

    2024年04月12日
    浏览(33)
  • TCP的粘包、拆包、解决方案以及Go语言实现

    TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中,比如RPC框架 在使用TCP进行数据传输时,由于TCP是基于字节流的协议,而不是基于消息的协议,可能会出现粘包(多个消息粘在一起)和拆包(一个消息被拆分成多个部分)的问题。这些问题可能会导致数据解析错误或数据

    2024年02月15日
    浏览(80)
  • 粘包/拆包问题一直都存在,只是到TCP就拆不动了。

    OSI open-system-Interconnection TCP/IP 5层协议栈 应用层和操作系统的边界是 系统调用 ,对应到网络编程是socket api TCP/UDP 概况 TCP粘包问题 TCP/IP报头深思 定义了网络框架,以层为单位实现协议,同时控制权逐层传递。 OSI实际并没有落地,TCP/IP 5层协议栈是目前主流的落地实现 。 TC

    2024年02月03日
    浏览(68)
  • 聊聊TCP协议的粘包、拆包以及http是如何解决的?

    目录 一、粘包与拆包是什么? 二、粘包与拆包为什么发生? 三、遇到粘包、拆包怎么办? 解决方案1:固定数据大小 解决方案2:自定义请求协议 解决方案3:特殊字符结尾  四、HTTP如何解决粘包问题的? 4.1、读取请求行/请求头、响应行/响应头 4.2、 怎么读取body数据呢?

    2024年02月11日
    浏览(41)
  • C++ Qt TCP协议,处理粘包、拆包问题,加上数据头来处理

    目录 前言: 场景: 原因: 解决: 方案2具体细节: 纯C++服务端处理如下: Qt客户端处理如下:         tcp协议里面,除了心跳检测是关于长连接操作的处理,这个在前一篇已经提到过了,这一篇将会对tcp本身的一个问题,进行处理:那就是做网络通信大概率会遇到的问题

    2024年02月04日
    浏览(51)
  • Socket TCP/IP协议数据传输过程中的粘包和分包问题

    一:通过图解法来描述一下分包和粘包,这样客户更清晰直观的了解: 下面对上面的图进行解释: 1.正常情况:如果Socket Client 发送的数据包,在Socket Server端也是一个一个完整接收的,那个就不会出现粘包和分包情况,数据正常读取。 2.粘包情况:Socket Client发送的数据包,

    2024年02月12日
    浏览(42)
  • 说说 TCP的粘包、拆包

    拆包和粘包是在socket编程中经常出现的情况, 在socket通讯过程中,如果通讯的一端一次性连续发送多条数据包,tcp协议会将 多个数据包打包 成一个tcp报文发送出去,这就是所谓的 粘包 。 如果通讯的一端发送的数据包超过一次tcp报文所能传输的最大值时,就会将 一个数据包

    2024年02月09日
    浏览(44)
  • Netty-LengthFieldBasedFrameDecoder-解决拆包粘包问题的解码器

    maxFrameLength:指定解码器所能处理的数据包的最大长度,超过该长度则抛出 TooLongFrameException 异常; lengthFieldOffset:指定 长度字段 的起始位置; lengthFieldLength:指定 长度字段 的长度:目前支持1(byte)、2(short)、3(3个byte)、4(int)、8(Long) lengthAdjustment:指定 长度字段 所表示的消息

    2024年02月12日
    浏览(36)
  • Netty编解码器,Netty自定义编解码器解决粘包拆包问题,Netty编解码器的执行过程详解

    当Netty发送或者接收一个消息的时候,就会发生一次数据转换。入站消息会被解码(从字节转换为另一种格式,比如java对象);出站消息会被编码成字节。 Netty 提供一系列实用的编解码器,他们都实现了 ChannelInboundHadnler 或者 ChannelOutboundHandler 接口。在这些类中,channelRead 方

    2023年04月23日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包