首页 > 编程知识 正文

tcp 4次挥手,tcp四次挥手过程

时间:2023-05-06 00:05:07 阅读:280496 作者:1521

在学习wireshark抓包的时候,一般都从最简单的三次握手和四次挥断看起,因为这两步对于每一个完整健康的TCP交互流来说都是必不可少的,通过抓包我们可以更清楚的了解其工作机制。

一、四次挥手和“三次挥手”

1、第一种情况

从抓包来看是很正常的三次握手和四次挥断

1.服务端FIN :Seq = a , Ack = b #我想断开连接
2.客户端ACK:Seq = b, Ack = a+1 #收到,断开吧
3.客户端FIN :Seq = b, Ack = a+1 #我也想断开连接
4.服务端ACK:Seq = a+1, Ack = b+1 #收到,断开吧

2、第二种情况

分别是:

1.客户端FIN :Seq = a , Ack = b #我想断开连接
2.服务端FIN :Seq = b, Ack = a+1 #收到,断开吧。我也想断开连接
3.客户端ACK:Seq = a+1, Ack = b+1 #收到,断开吧

这么看来是第二步和第三步合并了,那为什么会合并呢?

在俏皮的外套老师的《Wireshark网络分析就这么简单》一书中的第123页图11有出现过这种情况,其原因也在早年的一篇博客文章中也有提到,即 TCP的延迟确认 机制导致。

二、什么是TCP的延迟确认?

RFC 1122文档中的 4.2.3.2 When to Send an ACK Segment 小节有过对延迟确认机制下何时发送ACK的描述:

4.2.3.2 When to Send an ACK Segment
————
A host that is receiving a stream of TCP data segments can increase
efficiency in both the Internet and the hosts by sending fewer than
one ACK (acknowledgment) segment per data segment received; this is
known as a “delayed ACK” [TCP:5].
————
A TCP SHOULD implement a delayed ACK, but an ACK should not be
excessively delayed; in particular, the delay MUST be less than 0.5
seconds, and in a stream of full-sized segments there SHOULD be an ACK
for at least every second segment.
————
DISCUSSION:

A delayed ACK gives the application an opportunity to update the
window and perhaps to send an immediate response. In particular, in
the case of character-mode remote login, a delayed ACK can reduce the
number of segments sent by the server by a factor of 3 (ACK, window
update, and echo character all combined in one segment).
————
In addition, on some large multi-user hosts, a delayed ACK can
substantially reduce protocol processing overhead by reducing the
total number of packets to be processed [TCP:5]. However, excessive
delays on ACK’s can disturb the round-trip timing and packet
“clocking” algorithms [TCP:7].

简单的概括:TCP延迟确认机制,允许接受方在接收到数据帧时不马上回复ACK,而是可以等待一段时间再回复ACK,但这个时间不得大于0.5s(该时间可修改,Windows默认200ms,也可通过参数关闭机制)。

⭐RFC 1122规定,Delayed ACK对单个的小报文可以延长确认的时间,但不允许有两个连续的小报文不被确认。所以,当发送端连续发送两个报文后,接收端必须给予确认。(这点待讨论,很多抓包情况下确实有>2个包才发ACK的情况,这篇文章从代码上也对这个问题进行了讨论分析TCP delay ack机制和实现)

⭐这个延迟时间需要注意,它不是在接收到数据的时候开始计时,而是内核会启动一个200ms的定时器,每隔200ms就会检查一次自己是否有ACK需要发送给对方,比如定时器在0ms启动,200ms到期,100ms的时候接收到对方传来的数据,那么在计时器到200ms的时候我自己仍然没有数据需要发送,那我就该回复ACK了,这个情况下ACK延迟了100ms;而如果在150ms的时候刚好我有数据要发给对端,这时候就会将数据和ACK一并发送,这里的ACK只延迟了50ms。

那这个作用是什么呢?
一般来说TCP数据交互过程中,接收方A每收到发送方B发出的一个报文,接收方A需要回复发送方B一个ACK报文,告知我已收到这个包,可以发送下一个。但是在延时大或者拥塞的网络环境上,这种一来一回的交互缺点明显,ACK单独成包代价高。而在TCP延迟确认机制下有更优的表现:

即当客户端收到服务器发来的数据后,如果没有数据要发送给对端,则延迟一段时间再发送ACK,如果这段时间内有数据要发给服务器的话,会与ACK合并立即发送(ACK用于确认这段时间所收到的数据包),延迟确认没有直接提高性能,只是减少了部分确认包,减轻网络负担,当然这是一把双刃剑,某些情况下需要关闭。

回到正题,TCP延迟确认机制 对四次挥断也同样生效,第二步的服务端的ACK延迟到了和第三步服务端的FIN一并发送:服务器收到了客户端的FIN,等待一段时间看自己有没有数据要发给客户端,然后应用告知自己也想断开连接,于是FIN就和ACK一起发送

1.客户端FIN :Seq = a , Ack = b #我想断开连接
2.服务端FIN :Seq = b, Ack = a+1 #收到,断开吧。我也想断开连接
3.客户端ACK:Seq = a+1, Ack = b+1 #收到,断开吧

既然客户端都要结束会话了,服务端就不应该再发数据给客户端了吧?为啥还需要等待一段时间再ACK呢?

TCP链接是一个全双工的通道,客户端发送了FIN,只表明客户端要结束会话,即客户端自己不再发送数据,但是还可以继续接收数据的,同理因为服务器没有发FIN,服务器也可以继续发送数据。

那什么时候会四次挥断什么时候“三次挥断”呢?

看最先收到FIN的一方是否开启了延迟确认(delay ack),如果没有开启,正常情况下是不会等待FIN,需要立刻回复ACK的,即抓包能看到四个数据帧的挥断。

另提一下:
其实TCP不是通信的全部,TCP服务于application,即应用。 假设客户端A发起断开与服务端B的连接:
1.用户在客户端操作触发断链,此时A的application接受用户的断开请求,发送断链信号到A的TCP层;
2.于是A的TCP层发送FIN包➡B的TCP层;
3.B的TCP层接收到A的FIN,需要告知B的application:“A要断开TCP链接,你还有数据要给A的吗?” 然后等待application的回复,而且B的TCP层在收到FIN的同时立刻回复ACK给A,A➡B方向断开;
4.如果B的application有数据需要传输给A则继续发送,传完后B的application发送close给B的TCP层,告知可关闭链接;
5.于是B的TCP层发送FIN➡A的TCP层,A回复ACK后,B➡A方向也断开,结束。

这里参考了知乎车小胖老师的回答:https://www.zhihu.com/question/50646354

三、Nagle算法

有这么一种情况,我想搞个服务器来看看外面的世界,于是在国外买了一台服务器来搭VPN,从国内连过去后敲一串字符:fdfhaudifhidbfidvfaiudh,每个字符并不大,但是却需要封装成小包逐个发送出去,同时还得等待ACK,而且国内到国外时延这么大,体验太差了,那是否有机制允许这些小包合并成一个打包一起发送出去呢?有,Nagle算法。

百度百科中对其做了详细的解释:Nagle算法

为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。(一个连接会设置 MSS(单个报文的最大报文段长度) 参数,因此,TCP/IP希望每次都能够以MSS尺寸的数据块来发送数据)。Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。

Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段
所谓“小段”,指的是小于MSS尺寸的数据块,所谓“未被确认”,是指一个数据块发送出去后,没有收到对方发送的ACK确认该数据已收到。

Nagle算法的规则(可参考tcp_output.c文件里tcp_nagle_check函数注释):

(1)如果包长度达到MSS ,则允许发送; #即达到单个包最大值,此刻立即发出数据包
(2)如果该包含有FIN,则允许发送;
(3)设置了TCP_NODELAY选项,则允许发送;#打开TCP_NODELAY选项,则意味着无论数据包是多么的小,都立即发送(不考虑拥塞窗口)
(4)未设置TCP_CORK选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送;#当TCP_CORK选项被设置时,TCP链接不会发送任何的小包,即只有当数据量达到MSS时,才会被发送
(5)上述条件都未满足,但发生了超时(一般为200ms),则立即发送。

Nagle算法对传输的优化:

同样Nagle算法也有其弊端,即不适用于所有场景,像上图的这个例子,远程终端无法实时显示输入的字符,也无法通过TAB键来实时补全指令,因为它要收集齐一个MSS或者等超时后才发送给服务器。另在一些敏感业务和对实时数据要求高的场景,比如CSGO下,你看到敌人要偷袭你的队友,恰好你黄雀在后,准备重要的小松鼠,结果点了鼠标没反应,因为点击一次产生的数据没到一个MSS(单个报文的最大报文段长度),点击鼠标的指令没有发送给服务器,那你就等着被队友抽 四、Nagle算法+Delayed ACK的场景

结论是会产生 1 + 1 < 2 的效果,具体场景建议参考本文,有很详细的解释:TCP Nagle算法&&延迟确认机制

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。