tcp粘包拆包原因与解决方案

流楚丶格念 流楚丶格念     2022-12-09     539

关键词:

文章目录

TCP粘包和拆包

问题背景

TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据

TCP作为传输层协议并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。

当客户端与服务端进行TCP通信时,最理想的包的传输过程是这样的

举例说明

我从浏览器中访问了一个网站,网站服务器给我发了200k的数据。建立连接的时候,通告的MSS是50k,所以为了防止ip层分片,tcp每次只会发送50k的数据,一共发了4个tcp数据包。如果我又访问了另一个网站,这个网站给我发了100k的数据,这次tcp会发出2个包,问题是,客户端收到6个包,怎么知道前4个包是一个页面,后两个是一个页面。既然是tcp将这些包分开了,那tcp会将这些包重组吗,它送给应用层的是什么?这是我自己想的一个场景,正式一点讲的话,这个现象叫拆包

tcp中有一个negal算法,用途是这样的:通信两端有很多小的数据包要发送,虽然传送的数据很少,但是流程一点没少,也需要tcp的各种确认,校验。这样小的数据包如果很多,会造成网络资源很大的浪费,negal算法做了这样一件事,当来了一个很小的数据包,我不急于发送这个包,而是等来了更多的包,将这些小包组合成大包之后一并发送,不就提高了网络传输的效率的嘛。这个想法收到了很好的效果,但是我们想一下,如果是分属于两个不同页面的包,被合并在了一起,那客户那边如何区分它们呢?这就是粘包问题。

从粘包问题我们更可以看出为什么tcp被称为流协议,因为它就跟水流一样,是没有边界的,没有消息的边界保护机制,所以tcp只有流的概念,没有包的概念。

产生原因

我们知道TCP是以流动的方式传输数据的,传输的最小单位为一个报文段(Segment)。TCP Header中有个Options标识位。常见的标识位为MSS(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500bit,超过这个量要分成多个报文段,MSS则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460bit。换算成字节,也就是180多字节。

发生TCP粘包、拆包主要是以下原因:
(1)应用程序写入数据大于套接字缓冲区大小,会发生拆包;
(2)应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发送粘包;
(3)进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度——TCP header长度>MSS的时候会发生拆包;
(4)接收方法不及时读取套接字缓冲区数据,这将发生粘包。

解决方案

我们还需要知道两个概念:
(1)长连接: Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收。
(2)短连接:Client方与Server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。此种方式常用于一点对多点 通讯,比如多个Client连接一个Server。

实际,我想象的关于粘包的场景是不对的,http连接是短连接,请求之后,收到回答,立马断开连接,不会出现粘包。 但是拆包现象是有可能存在的

处理拆包这里提供两种方法:
(1)通过包头+包长+包体的协议形式,当服务器端获取到指定的包长时才说明获取完整。
(2) 指定包的结束标识,这样当我们获取到指定的标识时,说明包获取完整。

处理粘包我们从上面的分析看到,虽然像http这样的短连接协议不会出现粘包的现象,但是一旦建立了长连接,粘包还是有可能会发生的。

处理粘包的方法如下:
(1)发送方对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
(2)接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。

解决办法:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,判断每条数据的长度的方法有两种:
a. 格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
b. 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。

思考:UDP会不会产生粘包问题呢?

不存在粘包。

TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。

举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

tcp的粘包拆包问题+解决方案

 为什么TCP有而UDP没有粘包❓1️⃣因为udp的数据包有保护边界。2️⃣tcp是以字节流的形式,也就是没有边界,所以应用层的数据在传输层的时候就可能会出现粘包和拆包问题。出现这种问题的原因图解 查看详情

tcp粘包拆包

粘包、拆包发生原因:发生TCP粘包或拆包有很多原因,现列出常见的几点,可能不全面,欢迎补充,1、要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。2、待发送数据大于MSS(最大报文长度),TCP在传输前将进行... 查看详情

12.netty中tcp粘包拆包问题及解决方法(代码片段)

...文介绍了tcp粘包拆包问题;3.本文po出了粘包拆包问题解决方案及源代码实现;【1】tcp粘包拆包问题refer2HowtodealwiththeproblemofpacketstickingandunpackingduringTCPtransmission?-编程知识【1.1】粘包拆包 查看详情

12.netty中tcp粘包拆包问题及解决方法(代码片段)

...文介绍了tcp粘包拆包问题;3.本文po出了粘包拆包问题解决方案及源代码实现;【1】tcp粘包拆包问题refer2HowtodealwiththeproblemofpacketstickingandunpackingduringTCPtransmission?-编程知识【1.1】粘包拆包 查看详情

tcp粘包拆包基本解决方案

...粘包和拆包的现象,Netty对此问题提供了相对比较丰富的解决方案 Netty提供了几个常用的解码器,帮助我们解决这些问题,其实上述的粘包和拆包的问题,归根结底的解决方案就是发送端给远程端一个标记,告诉远程端,每... 查看详情

java网络编程——粘包拆包出现的原因及解决方式(代码片段)

在基于TCP协议的网络编程中,不可避免地都会遇到粘包和拆包的问题。什么是粘包和拆包?先来看个例子,还是上篇文章《Java网络编程——NIO的阻塞IO模式、非阻塞IO模式、IO多路复用模式的使用》中“IO多路复用模式... 查看详情

java网络编程——粘包拆包出现的原因及解决方式(代码片段)

在基于TCP协议的网络编程中,不可避免地都会遇到粘包和拆包的问题。什么是粘包和拆包?先来看个例子,还是上篇文章《Java网络编程——NIO的阻塞IO模式、非阻塞IO模式、IO多路复用模式的使用》中“IO多路复用模式... 查看详情

tcp粘包拆包

一、什么是粘包拆包?粘包拆包是TCP协议传输中一种现象概念。TCP是传输层协议,他传输的是“流”式数据,TCP并不知道传输是哪种业务数据,或者说,并不关心。它只是根据缓冲区状况将数据进行包划分,然后进行传输... 查看详情

netty是如何解决tcp粘包拆包的?

点击关注公众号,Java干货及时送达作者:rickiyang出处:www.cnblogs.com/rickiyang/p/11074235.html我们都知道TCP是基于字节流的传输协议。那么数据在通信层传播其实就像河水一样并没有明显的分界线,而数据具体表示什么... 查看详情

java网络编程——粘包拆包出现的原因及解决方式(代码片段)

在基于TCP协议的网络编程中,不可避免地都会遇到粘包和拆包的问题。什么是粘包和拆包?先来看个例子,还是上篇文章《Java网络编程——NIO的阻塞IO模式、非阻塞IO模式、IO多路复用模式的使用》中“IO多路复用模式... 查看详情

java网络编程——粘包拆包出现的原因及解决方式(代码片段)

在基于TCP协议的网络编程中,不可避免地都会遇到粘包和拆包的问题。什么是粘包和拆包?先来看个例子,还是上篇文章《Java网络编程——NIO的阻塞IO模式、非阻塞IO模式、IO多路复用模式的使用》中“IO多路复用模式... 查看详情

粘包拆包发生原因滑动窗口mss/mtu限制nagle算法

【TCP协议】(3)---TCP粘包黏包有关TCP协议之前写过两篇博客:   1、【TCP协议】(1)---TCP协议详解   2、【TCP协议】(2)---TCP三次握手和四次挥手一、TCP粘包、拆包图解假设客户端分别发送了两个数据包D1和D2给服... 查看详情

粘包拆包(分包)半包

粘包、拆包、半包理解TCP是一种面向流的网络层传输协议,在使用TCP作为传输层协议时,可保证数据的顺序性和可靠性。应用层在使用TCP协议传输数据时,可采取两种方式:短链接:客户端同服务端完成一次通信(客户端只发送... 查看详情

面试官:什么是netty粘包拆包?怎么解决netty粘包拆包问题

哈喽!大家好,我是小奇,一位热爱分享的程序员小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧文章持续更新一、前言书接上回,昨天肯定是狗蛋通风报信,导致... 查看详情

封装一个带大小的封包,防止tcp粘包拆包(代码片段)

C++头文件#ifndefTCPWRAP_H#defineTCPWRAP_H#include<memory>#include<netinet/in.h>#include<string.h>#pragmapack(push,1)typedefstructsize_tlength;//包头(包体长度)char*body;//包体Packet;#pragmapac 查看详情

netty之粘包拆包bytetomessagedecoder(代码片段)

目录拆包粘包处理的方式netty的处理方式netty实现编码解码1、cumulator.cumulate()拼接数据包2、callDecode()解析数据包总结LengthFieldBasedFrameDecoder拆包粘包粘包产生的原因:两个包小于缓存区的大小,传送数据会将两个包都放在... 查看详情

netty之粘包拆包bytetomessagedecoder(代码片段)

目录拆包粘包处理的方式netty的处理方式netty实现编码解码1、cumulator.cumulate()拼接数据包2、callDecode()解析数据包总结LengthFieldBasedFrameDecoder拆包粘包粘包产生的原因:两个包小于缓存区的大小,传送数据会将两个包都放在... 查看详情

netty之粘包拆包bytetomessagedecoder(代码片段)

目录拆包粘包处理的方式netty的处理方式netty实现编码解码1、cumulator.cumulate()拼接数据包2、callDecode()解析数据包总结LengthFieldBasedFrameDecoder拆包粘包粘包产生的原因:两个包小于缓存区的大小,传送数据会将两个包都放在... 查看详情