题目:
TCP是面向连接的协议,它确保了双方所收发的数据的正确和有序性。在利用TCP收发数据之前,必须通过双端的三个报文,即“三次握手”来确保连接的建立。这三个报文分别是:(想要建立连接的一端为A,另一端为B)
SYN
报文SYN+ACK
报文ACK
报文通信两端状态变化如下:
SYN
报文之前,A处于CLOSE
状态,B处于LISTEN
状态。A会初始化序号client_isn
,并将其置于SYN
报文的序列号中,并将其发送给B,示意A准备和B建立连接。发送完成后A处于SYN_SENT
状态。SYN
报文之后,B也会初始化自己的序列号server_isn
并置于准备回复给客户端的SYN+ACK
报文的序列号字段中;同时,确认应答号字段为A的client_isn + 1
。最后将该SYN+ACK
报文发送给A。此后B进入SYN_RCVD
状态。SYN+ACK
报文后,会回复BACK
报文,该报文内的确认序列号为B的server_isn + 1
,这次报文可以携带要传输的数据,此后客户端进入ESTABLISHED
状态。ACK
报文后,也进入ESTABLISHED
状态。这三次握手的最主要目的:
和三次握手相对的,即为断开连接时的四次挥手过程。它通过通信两端的四个报文来确保连接正确的断开。这四个报文分别是:(想要断开连接的一端为A,另一端为B)
FIN
报文ACK
报文FIN
报文ACK
报文通信两端状态变化如下:
ESTABLISHED
状态。FIN
报文过去,同时自己进入FIN_WAIT_1
状态。ACK
应答报文,同时自己进入CLOSED_WAIT
状态。FIN_WAIT_2
状态。FIN
报文,同时自己进入LAST_ACK
状态。ACK
报文,同时自己进入TIME_WAIT
状态。在该状态下经过两个MSL
时间段之后,如果没有报文到达,就自动进入CLOSE
状态,完成了连接的关闭。CLOSE
状态,完成了连接的关闭。这四次挥手的主要目的:
FIN
才表示同意关闭连接。客户端和服务端这几个状态的具体行为:
FIN_WAIT_1
:该状态下,A会一直等待B发送的应答报文,如果等待超时就重传,直到收到应答报文或者超出重传次数。如果超出重传次数就直接关闭连接。CLOSE_WAIT
:应答报文是不会重传的,因此该状态下,如果这个应答报文没有抵达A,那么A会重传FIN
报文,B依然会发送应答报文。在该状态下,B会继续发送原本要发送的数据,直到B的数据全部发送完毕,用户态调用close
进入下一个状态。LAST_ACK
:B在CLOSE_WAIT
状态下,B的用户进程调用close
进入该状态。B首先会发出一个FIN
报文,并且在该状态下等待A的确认报文。如果收到了来自A的确认报文,则关闭连接;如果超时则与A的FIN_WAIT_1
状态下的重传机制相同。FIN_WAIT_2
:A在收到B对第一次挥手的确认报文之后进入该状态。在该状态下,如果A的用户进程调用的是close
函数来关闭连接的话,该状态持续tcp_fin_timeout
时间之后自动关闭连接;如果A的用户进程调用的是shutdown
函数来关闭连接的话,该状态会一直持续到收到B的第三次挥手报文。TIME_WAIT
:A在收到B的第三次挥手报文后进入该状态。在该状态下A会先发送确认报文过去,之后经过两个MSL
时间段之后,如果没有报文到达,就自动进入CLOSE
状态。
MSL
(Maximum Segment Lifetime):报文最大生存时间,它是任何网络报文在网络上存在的最长时间。注意TTL
的单位是路由器跳数,而MSL
的单位是时间。默认情况下,Linux的MSL
是30秒。两个MSL
时间段,正好是报文一去一回的时间。
题目
TCP连接三次握手的过程,为什么是三次,可以是两次或者更多吗?
不可以。
原因一:两次握手就建立连接的话,不可能同时保证通信两端的收发能力。在第二次握手后,即使假设这两次握手的报文都能正确抵达目的地,那么在客户端的视角,能够确保了客户端的收发能力(第一次握手发出去,在第二次握手收到回应)和服务端的收发能力(第一次握手收到报文,第二次握手发送报文),但是在服务端的视角,是不能确认客户端的收信能力的。如果客户端在收到第二次握手之前就放弃建立连接,那么服务器就是在和一个不存在的客户端建立连接,浪费了服务器的资源。
原因二:避免历史连接。如果在第二次握手后,服务端就直接建立连接,那么服务端就没有机会确定要建立的连接的序列号是否正确,从而浪费资源。比如说客A户端在发送初始化client_isn
为90之后发送SYN
报文,之后在该报文还未抵达服务端时,客户端由于某种原因宕机并且马上恢复,再次初始化client_isn
为100之后发送SYN
报文,那么服务器要响应的就是这两个序列号不同的SYN
报文。按照假设,服务端收到第一个SYN
报文之后马上建立连接,那么通信两端的序列号就会出现不一致的情况,服务器不得不取消连接之后重新根据第二个SYN
报文建立连接,如此就会出现资源的浪费。
这两个原因总结下来只有一个关键问题:TCP建立连接的三次握手,目的是确保通信两端的收发能力,并且同步序列号,窗口大小等信息。如果少了任何一次握手,这两个目的都不能完成。第一个目的对应原因一,第二个目的对应原因二。如果不能保证这些就去建立连接,就有可能会导致资源的浪费。
至于为什么不能是四次握手,倒也不是说不能是四次,而是三次握手就能解决的问题,四次握手无异于浪费资源。
题目
TCP四次挥手的过程,为什么是四次?
首先先排除五次以上的可能性,既然四次已经跑的很好了,就没必要再多发报文了。
其次,这四次挥手,每个挥手都有自己独特的作用:前两次挥手确认主动断开那一端不再发送报文,后两次挥手确认被动断开那一端不再发送报文。如果想要降低挥手次数,无非两种方案:合并第二、三次挥手;或者取消第四次挥手。
先说第一种:合并第二、三次挥手,在以下条件下是成立的:即通信两端都没有开启TCP_QUICKACK
,同时被动关闭连接方确实没有后续数据要发送,则第二、三次挥手会被合并为一个报文。
本文作者:御坂19327号
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!