TCP三次握手与四次挥手

三次握手?

什么是三次握手?

一般情况下,连接是由客户端向服务端发起的。

第一次,客户端发送一个TCP数据报并将SYN同步位置为1,表示要建立连接,此时客户端会从CLOSED状态变为SYN_SEND状态;

第二次是服务端向客户端发送ACK,并且也将SYN置为1,一是表示自己收到了客户端建立连接的请求,并且从LISTEN状态变为SYN_RCVD,二是表示自己要建立连接;

当客户端收到服务端的ACK + SYN后,便会处于ESTABLISHED状态,并且发送第三次握手,表示自己收到了客户端的SYN。当服务端收到客户端的ACK后,便会从SYN_RCVD状态变为ESTABLISHED状态。

此时,双方的连接才算真正的建立。

为什么需要三次握手,而不是两次握手或者四次握手?

1.双方之所以进行多次握手,本质上还是因为信道是不可靠的。

由于TCP本身的要求是可靠连接,因此,客户端和服务端都必须确认,对方的收发能力都是正常的。因此,当第二次握手结束,客户端就可以确保服务端的收发能力正常,建立起连接,但是,服务端只能确保客户端的发送能力正常,因此必须等收到第三次握手的ACK后才会建立连接。

理论上四次五次都可以,但是三次就可以保证,所以是三次握手。

2.最好是奇数次握手

在socket编程时,服务端一般是先listen再accept。也就是说,发起连接的都是客户端。

谁发送最后的ACK,谁就会先建立连接。如果是偶数次,那么最后一次发送ACK的就是服务端。一般情况下,服务器比客户端的数量是1:n,众所周知,维护连接是需要付出时间和空间成本的,比如需要在内核建立相关结构体,如果服务器建立连接而客户端未建立,那么服务端可能会有多个空连接,这其实是不利于服务器的。

奇数次的握手是一种成本转移,即便出现空连接的现象,成本也会分摊在多个客户端身上,而不至于给服务端太大压力。

四次挥手

什么是四次挥手?

四次挥手与三次握手对应,是断开连接的过程。

由于双方之前进行了大量数据的交互,因此通过发送与确认机制就可以保证双方连接的断开。

首先断开的一方会发送FIN,便会处于FIN_WAIT_1状态,只要收到ACK后,便会处于FIN_WAIT_2状态。表示自己关闭了发送缓冲区,不再发送数据,但依然具有接受数据的能力。

而另一方收到FIN后,便会处于CLOSE_WAIT状态,即半关闭状态。它还可以继续发送数据,当数据发送完毕后,又会发送FIN,当收到ACK后便关闭自己的发送缓冲区。

这就是四次挥手。

为什么是四次?

我们在写客户端或者服务端时,都有一个close(fd),即关闭文件描述符的操作。每一个close代表的就是两次挥手。

如果说只有两次,也就是说一方在执行代码逻辑时没有执行close(fd),第一,会造成文件描述符泄露,第二,会发现这一端存在CLOSE_WAIT状态的连接,即半关闭状态。这种状态并没有释放连接相关的数据和结构体,其实也会造成内存泄漏。

为什么四次挥手后,主动断开连接的一方会先进入TIME_WAIT状态?

四次挥手的前三次,由于超时重传机制的存在,可以保证一定被对方收到,但是最后一次的ACK,是可能丢失的。如果四次挥手后,主动断开的一方直接CLOSED,那么将无法保证最后一个ACK被对方收到。

如果ACK丢失,对方就会重新发送FIN,如果处于CLOSED状态,将不会收到FIN,使得对方长时间处于LAST_ACK状态,并且多次发送FIN消耗资源。因此,通过TIME_WAIT,可以较大概率地保证最后一个ACK被对方收到。

另一个原因是可以保证信道上的数据尽可能的消散,防止断开连接后又立马重新建立连接后,产生的数据错误等问题。

为什么TIME_WAIT是2MSL?

2MSL是两个最大报文段寿命,ACK丢失最多是一个MSL,再次收到重传的FIN最多是一个MSL,因此2MSL可以尽可能的保证在CLOSE之前收到对方重传的FIN。