文章同步发表在朗度云,传输门:
http://www.wolfbe.com/detail/201608/336.html
也许三次握手你会经常听到,但你知道三次握手的真正意义吗,为什么需要三次握手呢?
首先我们必须明白TCP是面向连接的协议,无论哪一个方向在发送数据之前,都必须先在双方之间建立连接。这一点与UDP协议是不一样的,UDP在发送数据报之前是不需要建立连接的。建立TCP连接的过程中,通信的双方需要互相发报文进行对话,总共发了三次报文,我们把这个过程称为三次握手,三次握手的过程也就是TCP建立连接的过程。
既然三次握手的过程需要用到报文,我们先了解一下报文的格式吧。下图中的报文格式即是一般的TCP报文模板,每一行表示32位(即4字节)长度,TCP报文由首部与数据组成,其中首部由固定选项和可选选项组成,固定选项占用20个字节。
图1 TCP报文格式
在三次握手的过程中,我们需要了解的是报文的序号、确认号、标志位,如下:
- 序号:表示报文段中的第一个数据字节,序号用来标识从TCP报文发送端向接收端发送的数据字节流,TCP用序号对每个发送的字节进行计数,或者说序号的值表示该端成功发送的数据字节数(忽略初始序列号ISN);
- 确认序号:表示当前端成功接收到的数据字节数(忽略初始序列号ISN);
- SYN标志:表示发起一个连接;
- ACK标志:表示确认号有效;
- FIN标志:表示释放一个连接;
TCP连接的三次握手
图2 TCP连接三次握手
我们假设建立TCP连接的请求由Client发起,那么整个建立TCP连接流程如下:
- Client向Server发送一个SYN报文,其中报文标志SYN=1,序号SEQ=J,发送报文后Client处于SYN_SENT(同步已发送)状态;TCP规定,SYN报文不能携带数据,但标志SYN会消耗一个序号;
- Server接收到SYN报文,如果同意建立连接,那么Server会发送SYN+ACK报文,报文的标志SYN=1、ACK=1,序号SEQ=K,确认序号ACK=J+1,发送报文后Server处于SYN_RCVD(同步已收到)状态
- Client收到SYN+ACK报文后,需要向Server发送ACK报文确认,报文的标志ACK=1,确认序号ACK=K+1,此时Client进入ESTABLISHED(连接已建立)状态;当Server接收到ACK报文后,Server也进入ESTABLISHED(连接已建立)状态。
为什么要Server需要接收到Client的ACK报文后才建立连接呢,而不是在收到SYN+ACK报文后就建立连接??
主要是为了防止已失效的SYN报文又回到Server。因为有可能出现这样的情况: Client发出SYN报文后,由于某些原因在网络中滞留了一段时间,以致于延迟到连接已经释放后才到达Server。Server收到这个已经失效的SYN报文,误以为这是Client发起一个新的连接请求,Server会向Client发送确认的SYN+ACK报文,同意建立连接。如果不采用三次握手,那么Server在发出SYN+ACK报文后就建立新的连接了。由于Client没有发起新的连接请求,因此它不会理会Server建立连接的SYN+ACK报文,也不会向Server发送任何数据。此时,Server误以为连接已经建立,一直在等待Client发送数据,这样Server因为等待无效的连接的很多资源就白白资源了。采用三次握手就可以防止上面的情况出现。
TCP释放的四次挥手
图3 TCP释放四次挥手
由于TCP是全双工的,因此每个方向必须单独进行关闭。这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接;收到一个FIN表示这一方向上不会再有数据流动了,即不会再收到数据了,但自己仍可以向对方发送数据。首先发起FIN请求的一方执行主动关闭,而另一方执行被动关闭。我们假设Client首先发起FIN请求,流程如下:
- Client向Server发送FIN报文请求关闭连接,其中报文标志FIN=1,序号SEQ=M,标记FIN会消耗一个序号,然后Client进入FIN_WAIT_1状态;
- Server收到FIN报文,向Client发送ACK报文,其中报文标志ACK=1,确认序号=M+1,Client收到报文后进入FIN_WAIT_2状态,等待Server发出释放连接报文;
- Server完成数据发送任务,向Client发送FIN报文,其中报文标志FIN=1,序号SEQ=N,然后Server进入LAST_ACK状态;
- Client收到Server的FIN报文,向Server发送确认报文,确认报文的标记ACK=1,序号ACK=N+1,然后Client进入TIME_WAIT状态;Server收到确认报文后,释放连接资源,进入CLOSED状态,此时Server->Client方向的连接关闭,Client->Server方向的连接必须等待一段时间后才会执行关闭,一般为2MSL,MSL即报文最大生存时间。
为什么Client在TIME_WAIT状态上必须等待一段时间才进入CLOSED状态呢?
- 为了保证Client发送的ACK报文能够到达Server。因为最后Client发送的ACK报文有可能丢失,因而使处在LAST_ACK状态的Server收到Client的确认,Server会超时重传FIN报文,而Client就能在2MSL时间内收到这个重传的FIN报文,接着Client再次发送ACK报文确认,重新启动2MSL计时器,经过2MSL时间后Client若没有再次收到Server报文,那么就可以认为Server已经关闭了,Client也可以关闭自己方向的连接了。
- 防止已失效的报文出现在新的连接报文中;Client在发送完最后一个ACK报文后,经过2MSL时间就可以使本连接持续时间内所产生的所有报文都从网络中消失,这样在下一个新的连接中就不会出现旧连接中的连接报文。