在学习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会被发送,这个情况下只延迟了100ms.
- 那这个作用是什么呢?
一般来说TCP数据交互过程中,接收方A每收到发送方B发出的一个报文,接收方A需要回复发送方B一个ACK报文,告知我已收到这个包,可以发送下一个。但是在延时大或者拥塞的网络环境上,这种一来一回的交互缺点明显,ACK单独成包代价高,而在TCP延迟确认机制下有如下表现: 即当客户端收到服务器发来的数据后,如果没有数据要发送给对端,则等待一段时间,如果这段时间内有数据要发给服务器的话,会与ACK合并发送(确认这段时间所收到的数据),延迟确认没有直接提高性能,只是减少了部分确认包 ,减轻网络负担,当然这是一把双刃剑,某些情况下需要关闭。
回到正题,TCP延迟确认机制 对四次挥断也同样生效,第二步的服务端延迟到了和第三步一并发送,服务器收到了客户端的FIN,等待一段时间看自己有没有数据要发给客户端,然后自己也想断开连接,FIN就和ACK一起发送:
1.客户端FIN :Seq = a , Ack = b #我想断开连接 2.服务端FIN :Seq = b, Ack = a+1 #收到,断开吧。我也想断开连接 3.客户端ACK:Seq = a+1, Ack = b+1 #收到,断开吧
-
那既然要结束会话了,为什么服务端还要等待发送数据呢? TCP链接是一个全双工的通道,客户端发送了FIN,只表明客户端要结束会话,即自己不再发送数据,但是还可以继续接收数据,因为服务器没有发FIN,服务器也可以继续发送数据。 -
那什么时候会四次挥断什么时候“三次挥断”呢? 看被动断开TCP链接的系统是否开启了延迟确认(delay ack),如果没有开启,正常情况下是不会等待FIN,需要立刻回复ACK的,即四次挥断。 -
另提一下: 其实TCP不是通信的全部,TCP服务于application,即应用。 假设客户端A发起断开与服务端B的连接: 1.用户在客户端操作触发断链,此时A的application接受用户的断开请求,信号到达A的TCP层; 2.于是A的TCP层发送FIN包?B的TCP层; 3.B接收到A的FIN,需要告知B的application:“A要断开TCP链接,你还有数据要给A的吗?” 然后等待application的回复,在收到FIN的同时立刻回复ACK给A,A?B方向断开; 4.如果有数据需要传输则继续发送,传完后application发送close给B的TCP层,告知可关闭链接; 5.于是B的TCP层发送FIN?A的TCP层,A回复ACK后,B?A方向也断开,结束。
参考知乎车小胖老师的回答:https://www.zhihu.com/question/50646354
三、Nagle算法
有这么一种情况,我在国外买了一台服务器搭VPN,从国内连过去后敲一串字符:fdfhaudifhidbfidvfaiudh,每个字符并不大,但是却需要封装成小包逐个发送出去,那是否有机制允许这些小包合并成一个打包一起发送出去呢?有,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下,你看到敌人要偷你的队友,恰好你黄雀在后,想来个headshot,结果点了鼠标没反应,没到一个MSS,点击鼠标的指令没有发送给服务器,那你就等着被队友抽
四、当Nagle算法遇到Delayed ACK
建议参考本文,有很详细的解释:https://my.oschina.net/xinxingegeya/blog/485643
|