所谓三次握手
指的是在TCP连接建立时,建立连接的两台主机之间会发送三个数据包,用来建立有效的连接。
第一次握手:主机A请求连接,向主机B发送数据包,主机A状态从CLOSED转为SYN-SENT,本次发送数据包头部有以下特点:
SYN
标志为1,表示请求建立连接。ACK
标志为0,表示该数据包不是回应数据包。序列号码
用于标识当前数据包的位置,SYN标志为1时,是随机生成的初始序列号ISN,假设为x。第二次握手:主机B收到主机A数据包后回一个数据包给主机A,主机B状态从LISTEN转为SYN-RECEIVED,本次发送数据包头部有以下特点:
ACK
标志为1,表示该数据包是回应数据包。确认号码
为x+1,表示该数据包是回应主机A发送的数据包序列号码
为x的回应数据包,也就是第一次握手发送的数据包。SYN
标志为1,表示请求建立连接。序列号码
用于标识当前数据包的位置,SYN标志为1时,是随机生成的初始序列号ISN,假设为y。第三次握手:主机A收到主机B回应的数据包后,再回一个数据包给主机B,主机A状态从SYN-SENT转为ESTABLISHED,本次发送数据包头部有以下特点:
ACK
标志为1,表示该数据包是回应数据包。确认号码
为y+1,表示该数据包是回应主机B发送的数据包序列号码
为y的回应数据包,也就是第二次握手发送的数据包。SYN
标志为0,表示这不是请求建立连接的数据包。序列号码
用于标识当前数据包的位置,本次握手为x+1。当主机B收到第三次握手后,状态从SYN-RECEIVED转为ESTABLISHED,三次握手完成。这是建立TCP连接最常见、也是最正常的情况,但是现实中的网络情况错综复杂,并不是每次建立连接都这么理想,按照一问一答这样的顺序来完成,使用三次握手的方式来建立连接,就是为了尽可能的建立正确有效的连接。
参考:TCP协议头部结构
注释比如说,当主机A向主机B第一次发送建立连接请求时,由于网络延迟导致超时,然后主机A重新尝试,第二次发送建立连接请求,但是还没等第二次请求发送到主机B,第一次发送请求先达了主机B,对于主机B来说,这是第一次接收到连接请求,所以直接回复连接请求,那么主机A收到回复之后会如何处理呢?过程如下图所示。
当主机A接收到主机B的回复时,发现确认号码
不是101,而是91,就会回复主机B一个序列号码
为91且RST
标志为1的数据包,当主机B收到RST
数据包后就会从SYN-RECEIVED恢复到LISTEN状态,当第二次发送的序列号码
为100的建立连接请求达到主机B后,再从头开始正常的三次握手流程。
如果上面的情况改一下,第二次发送的建立连接请求先达了主机B,并且顺利完成了连接,此时才收到第一次发送的建立连接请求,会发生什么情况呢?过程如下图所示。
当主机B收到一个意外的请求时,因为主机B已经处于ESTABLISHED状态,并且正在接收的这个数据包不在接收窗口之内,此时它只会回复一个包含当前确认号码
的数据包,也就是<SEQ=201><ACK=101><CTL=ACK>。
除了上面提到的两种场景,还有很多非正常的场景。上面提到了三次握手是为了尽可能的建立正确有效的连接,试想一下如果只有两次握手,该如何处理各种非正常情况,那为什么不四次握手,我个人觉得四次握手在某些特殊场景下可能比三次握手更有效,但是对于大多数情况而言,三次握手就足够了,多一次握手会增加额外的开销,所以三次握手是综合考虑各种因素的最终选择。