TCP的三次握手与四次挥手理解

一、TCP/Socket的11种状态 

1、客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT

2、服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK

3、共有的:(1)CLOSED (2)ESTABLISHED

各个状态的意义如下: 

LISTEN - 侦听来自远方TCP端口的连接请求; 

SYN-SENT - 在发送连接请求后等待匹配的连接请求; 

SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认; 

ESTABLISHED - 代表一个打开的连接,数据可以传送给用户; 

FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

FIN-WAIT-2 - 从远程TCP等待连接中断请求; 

CLOSE-WAIT - 等待从本地用户发来的连接中断请求; 

CLOSING - 等待远程TCP对连接中断的确认; 

LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; 

TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认; 

CLOSED - 没有任何连接状态。

二、TCP三次握手(图片来自:https://www.jianshu.com/p/6b159058686e

1、字段说明:

序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生;给字节编上序号后,就给每一个报文段指派一个序号;序列号seq就是这个报文段中的第一个字节的数据编号,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。

确认号ack:占4个字节,期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。

确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效。

同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建立连接时才会被置1,握手完成后SYN标志位被置0。

终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求结束传输连接。

PS:ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号,seq是数据包本身的序列号;ack是期望对方继续发送的那个数据包的序列号。

2、握手过程:

第一次握手:客户端A将标志位SYN置为1,随机产生一个值为seq=x的数据包发送给服务器,客户端A进入SYN_SENT状态,等待服务端B确认;

第二次握手:服务端B收到数据包后由标志位SYN=1知道客户端A请求建立连接,服务端B将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端A以确认连接请求,服务端B进入SYN_RCVD状态。

第三次握手:客户端A收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,客户端A自己此条消息的序列号是x+1,所以seq=x+1,并将该数据包发送给服务端B,服务端B检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,客户端A和服务端B进入ESTABLISHED状态,完成三次握手,随后客户端A与服务端B之间可以开始传输数据了。

3、为什么不能用两次握手进行连接?

三次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

(1)假设客户端给服务端第一次发送请求过程中就出了问题:

在谢希仁著《计算机网络》第四版中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。书中的例子是这样的,“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”。主要目的防止server端一直等待,浪费资源。

(2)假设服务端给客户端第一次响应过程中出了问题:

现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组,这样就形成了死锁。

三、TCP四次挥手(图片来自:https://www.jianshu.com/p/6b159058686e

1、挥手过程:

第一次挥手:Client发送FIN=1,序列号seq=u给Server,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

第二次挥手:Server收到FIN后,发送一个ACK=1给Client,同时Server告诉Client自己的初始序列号,也就是seq=v,确认序号为收到的序列号(u)+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

第三次挥手:Server发送FIN=1,ACK=1,自己的第二个序列号seq=w,确认序号u+1给Client,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK=1给Server,确认序号为收到序号(w)+1,Server进入CLOSED状态,完成四次挥手。

2、为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

因为当Server端收到Client端的SYN连接请求报文后,把ACK和SYN放在一个报文里,可以直接给客户端发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,仅仅表示Client端不再发送数据了但是还能接收数据,Server端也未必将全部数据都发送给Client端了,所以Server端可以立即close,也可以发送一些数据给Client端后,再发送FIN报文给Client端来表示同意现在关闭连接,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此ACK和FIN不能一起发送。故需要四步握手。

3、为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这是因为双方虽然都同意关闭连接了,并且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态;但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

 

本文参考自:

(1)https://www.cnblogs.com/xianyulaodi/p/6547807.html

(2)https://blog.csdn.net/qq_38950316/article/details/81087809

(3)https://blog.csdn.net/seaSkyOK/article/details/79929525

 

原文地址:https://www.cnblogs.com/opsprobe/p/11685084.html