关于tcp粘包tcp协议对应用层多次请求的消息边界处理
Posted lesion__
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于tcp粘包tcp协议对应用层多次请求的消息边界处理相关的知识,希望对你有一定的参考价值。
写在前面
应用层应用向另一端的应用层发送一次信息,这个信息的内容我们称之为包(应用层报文)。包是应用层的概念,所以网络上很多对于tcp粘包的解释是有误的。
一次理想的tcp请求
首先我们梳理一个非常理想的情况中一次tcp连接发送多个包的过程:
1.发送方应用层将本次要发送的包放到tcp连接的发送缓冲区(对应socket编程中的send函数)。
2.tcp用发送缓冲区的包构建tcp报文,将该应用层报文发送到tcp连接另一端的接收缓冲区。
3.另一端的接收缓冲区提醒应用层消息的到来,应用层取出接收缓冲区的所有信息(对应socket编程中的recv函数)。
4.发送方应用层将第二次要发送的包放到tcp连接的发送缓冲区(对应socket编程中的send函数)。
5.tcp用发送缓冲区的包构建tcp报文,将该应用层报文发送到tcp连接另一端的接收缓冲区。
6.另一端的接收缓冲区提醒应用层消息的到来,应用层取出接收缓冲区的所有信息(对应socket编程中的recv函数)。
…
应用层请求和tcp请求的关系?
下面的问题是等价的:我们能否依靠接收方调用了几次recv函数来判断发送方发了几次包呢? / 调用了一次recv是否就代表发送方发送了一次包呢? / 假如我是服务端,我们能否以调用recv的次数作为另外一方发送请求的次数? / 假如我是服务端,是否一次recv就代表对方发来了一次请求且recv接收到的内容就是这次请求的内容?
答案是否定的,tcp协议并不维护消息(包、应用层报文)边界,接收方一次接收并不代表发送方发送了一次应用层报文,并且接收方一次recv接受到的内容既可能包含发送方多个应用层报文,又可能仅有一个报文的一部分,还可能有一个报文的完整信息加上一个报文的部分信息。这种一次recv并不一定按照理想情况代表着发送方应用层的一次请求,接收到的内容也不是一个应用层报文的情况,我们称之为粘包。
发生粘包的原因是多种多样的:
1.如果发送方发送一次应用层报文,该应用层报文的内容长度很长,长度超过了一个tcp数据段能够承载的最大长度,则tcp协议在发送该应用层报文的时候会截取该应用层报文的一部分发送,另外一部分会等待下一个tcp报文将其发送给接收方。此时如果没有其他干扰的话,接收方接收到的报文就只是一次应用层报文发送的一部分。
2.无论tcp策略是什么,每一个tcp报文在发送之前都会等待某一个ack的到来再进行发送,假如发送方原本打算发送一次应用层报文,在tcp报文等待到所需的ack之前,应用层又发来一个应用层报文,则本次tcp报文会在数据段承载两个应用层报文。此时如果没有其他干扰的话,接收方接收到的报文就是应用层两次发送的报文的内容。如果两个应用层报文的长度超过了一个tcp数据段能够承载的最大长度,没错,会截断该后一个应用层报文然后再发送。
3.tcp报文到达接收方的缓冲区之后会提醒应用层来取缓冲区的内容,如果在接收方应用层来取缓冲区的内容之前到达了新的tcp报文,则这些tcp报文会首位相接放进缓冲区,然后被接收方的应用层一次取出。
综上所述,由于tcp协议并不维护消息边界,tcp接收方应用层调用一次recv接收到的内容可能是残缺的应用层请求、可能是多个应用层请求、可能是若干个完整的应用层请求加一个残缺的应用层请求。正是这个原因,一个严谨的应用层应用需要定义自己的格式能够区分发送方发来的多次请求,能够在recv的内容是残缺的应用层请求报文时被发现并等待下一个recv将其拼接完整,能够在recv一次接收到多个应用层请求时区分开这些请求然后一一处理。这个格式在redis中是由*和$标识的参数个数和长度,在http协议中是由content-length或Transfer-Encoding标识的报文结束等。
这样做带来的特性
如果一个严谨的应用层应用实现了上述功能,则意味着一件事:这个网络应用能够正常处理下面这些情况发送的请求:
1.原本应该由一次应用层请求发送的应用层请求报文内容,被分为多次应用层请求发送,网络应用依然能够正确处理。
这里也可能会设置接收到一个残缺的应用层报文之后等待一定的时间,如果超出一定时间还是没有recv的话就返回报文格式出错的处理。
2.原本应该由多次应用层请求发送的应用层请求报文内容,被塞进一个应用层请求发送,网络应用依然能够正确处理。
其实第2点还有一个额外的要求:网络应用支持在上一个应用层请求处理结束之前就让发送方发送其他应用层请求。这在http中被称为pipline。
总结一句话,这些网络应用只看发送方发来的数据流,并不关心这些数据流是由多少次应用层请求用什么样的方式发送过来的。
正是由于这种特性的存在,我们才有底气在利用ssrf攻击redis、mysql等应用时,将原本多次发送的应用层报文的内容全部塞进一次ssrf请求。
也正是由于这种特性的存在,我们才有底气构造http请求走私的payload。
以上是关于关于tcp粘包tcp协议对应用层多次请求的消息边界处理的主要内容,如果未能解决你的问题,请参考以下文章