后文有个术语叫做 MSL,这儿先解释一下。
MSL 全称叫做 Maximum Segment Lifetime 即最大报文生存时间,它是一个数值,表示了一个报文在整个网络中最多能存活多久,防止消息一直在网络中驻留。
Linux 系统可以使用cat /proc/sys/net/ipv4/tcp_fin_timeout
查看其值,一般是 60 或者 120
后文还有个术语叫做四元组,其实就是 TCP 报文的
源IP
,目的IP
,源端口
,目的端口
,在 TCP 交互中只要发现四元组是相同的,那么就会认为是属于同一条 TCP 连接中的数据
三次握手
why?
需要三次握手而不是两次握手可以通过一个例子知道缘由:
假设通过两次握手就能建立连接,那么可能会出现这么一种情况。
客户端A向服务端发送SYN报文,请求建立连接
服务端B收到了这个SYN报文,并且也向A发送ACK报文,这时候服务端就进入了连接建立状态,于是开始等待客户端A说话发消息
but,很不幸的是,服务端B收到A发来的SYN报文其实是很久之前的消息,可能是因为信道堵塞或者其他奇奇怪怪的原因,导致这个SYN报文姗姗来迟。服务端就永远也等不到客户端的下文了
在教材《计算机网络》中的表述是:
采用三次握手是为了防止已失效的连接请求又传送到服务器端,因而产生错误
但其实不太准确,还有如下理由:
为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认
题外话
在服务端进入 SYN_RECD 状态之后,收到客户端最后一个 ACK 之前,这个时候的连接称之为半连接,服务端会使用一个队列来存储这些半连接
如果客户端搞事情,在短时间内伪造大量不存在的 IP 地址,向服务器不断地发送 SYN 包,服务器回复确认包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时,这些客户端伪造的 SYN 包将长时间占用未连接队列,正常的 SYN 请求被丢弃,而且还会导致服务端半连接队列被撑爆,无法就收新的半连接,也就更无法建立新的 TCP 连接
四次挥手
四次挥手而不是三次挥手的原因:
需要给被动断开连接的一方留出一段时间,以便它处理完最后的数据。在图中就是指的 CLOSE_WAIT
到 LAST_ACK
之间的时间
那么还有一个问题,为什么主动断开方还需要一个 TIME_WAIT 状态,并且等待 2MSL 时间后才真正的断开连接呢?
为了便于描述,我们设想这么一个处于拆链 也就是断开TCP连接的过程
:
这个连接的两端分别是A和B,A是主动发起断开请求的一方。
A刚发送完最后一个ACK报文,进入到了TIME_WAIT状态,
B此时处于LAST_ACK状态,等待A的最后一个ACK报文到来。
随着时间的推移,接下来A发送给B的ACK有两种结局:
1.ACK报文在网络中丢失。这种情况我们不需要关注,因为有重传机制
2.B接受到ACK报文。
我们假设A发送了ACK报文后过了一段时间t之后B才收到该ACK,则肯定有 0 < t <= MSL。
因为A并不知道它发送出去的ACK要多久对方才能收到,所以A至少要维持MSL时长的TIME_WAIT状态才能保证它的ACK从网络中消失。同时处于LAST_ACK状态的B因为收到了ACK,所以它直接就进入了CLOSED状态,而不会向网络发送任何报文。
所以晃眼一看,A只需要等待1个MSL就够了,but仔细想一下其实1个MSL是不行的,因为在B收到ACK前的一刹那,B可能因为没收到ACK而重传了一个FIN报文,这个FIN报文要从网络中消失最多还需要一个MSL时长,
所以A还需要多等一个MSL
简单粗暴的说,最后等待 2MSL 时间就是为了以静默的方式 吃掉
对方的数据,避免对后续的连接造成影响
什么情况下会对后续连接造成影响呢?
假设 A 端并没有等待 2MSL 这个操作,直接关掉了连接,B 端也关掉了连接。恰好 B 端有一个报文 xxx 因为信道堵塞,在 A 端关闭连接后还存在,没有被 A 端静默吃掉。
啪的一下,很快啊,B 端又重新建立了一个连接,并且其四元组还和上一次的相同,那么之前的那个报文 xxx 慢悠悠的到达了 A 端后,A 端会认为是这次新 TCP 连接中的消息。。这不就搞出脏数据了嘛
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于