详解TCP三次握手/四次挥手

详解TCP三次握手/四次挥手

连接管理机制:
建立连接是可靠传输的前提,唤起对方的注意

客户端与服务器之间没有明确的界限,
谁先发起请求,谁就是客户端

三次握手:

三次握手的目的是双方确认自己与对方能够正常发送和接收数据,即建立可靠的TCP连接,同时同步双方的序号和确认号并交换TCP窗口大小信息。
三次握手(Three-way Handshake),指在建立TCP连接时,需要通信双方一共发送三个数据包。
在socket编程中,客户端执行connect()时,将触发三次握手
这里写图片描述
- 第一次握手:客户端向服务器发送带有SYN标志的TCP报文,表示建立连接请求。
- 第二次握手:服务器收到SYN报文段后,需要对这个SYN报文段进行确认,同时自己还要发送SYN请求信息,来与客户端建立连接。因此,服务器将SYN+ACK标志放到一个报文段中,一并发给客户端
- 第三次握手:客户端收到服务器发来的SYN+ACK报文后,需要进行对服务器的连接请求进行确认,即发送一个带有ACK的报文段给服务器,发送完毕后,客户端和服务器都进入ESTABLISHED状态。

这里我们要重点理解两个状态:
1.LISTEN:listen()函数调用后,服务器处于被动状态,等待客户端来建立连接。(好比手机开机,信号良好)
2.ESTABLISHED:通信双方完成了三次握手,处于稳定连接的状态(好比电话拨通,等待说话),进入这种状态后双方就能够发送数据了

♥♥♥ 为什么不是两次握手,而是三次握手?

假设有这么一种情况,客户端向服务器发送了建立连接的请求,但是由于网络原因长时间滞留在某处,此时客户端迟迟收不到来自服务器的确认报文,于是会重传一次连接请求,重新建立连接。若一段时间后,第一次客户端发送的请求报文到达了服务器,这本来就是一个早已经失效的报文段,但是服务器却不知情,以为是客户端要来建立新的连接,就会回应一个确认报文同意建立连接。
若采用的是两次握手机制,此时连接就稳定建立了,由于客户端并没有发送连接建立请求,也就没有数据要向客户端发送,而服务器会一直等待对端发来数据,这样就会浪费服务器资源。
三次握手机制就会避免上述问题的发生,客户端并不想要建立连接,即使收到服务器的报文,也不会发送确认报文,那么服务器收不到来自客户端的确认,也就懂了对方的意思。

四次挥手:

是通信双方断开连接的过程。
这里写图片描述

  • 第一次挥手:主机1调用close()后,向主机2发送FIN报文,表示它已经没有数据要发送了,进入FIN_WAIT1状态,但这时主机1还是可以接收来自主机2的数据。
  • 第二次挥手:主机2收到主机1发来的FIN报文后,先发送一个ACK报文,表示已收到FIN,知道主机1发送完数据了,接着进入CLOSE_WAIT状态,主机1收到来自主机2的ACK后进入FIN_WAIT2状态。
  • 第三次挥手:主机2调用close()后,向主机1发送FIN报文,表示自己发送完数据,请求关闭连接,同时进入LAST_ACK状态。
  • 第四次挥手:主机1收到来自主机2的FIN后,向主机2发送ACK报文同时进入TIME_WAIT状态,主机2收到主机1的ACK后就关闭连接,主机1等待2MSL后依然未收到回复,说明主机2已经正常关闭,这时,主机1就可以放心的关闭连接了。

建立请求时先发送请求的一定是客户端,而断开时双方都有可能主动发起,任何一方执行close,即可产生挥手操作。

几个重要的状态:

  • FIN_WAIT1/FIN_WAIT1:这两个状态都是等待对方的FIN报文,FIN_WAIT1是在ESTABLISHED状态下,一端想要主动断开连接,就向对方发送 FIN进入FIN_WAIT1状态。当对方回应ACK后,则进入FIN_WAIT2状态。FIN_WAIT1状态一般比较难见到,因为不管对端什么处于情况,一旦受到FIN,内核会立马回复一个ACK报文。而FIN_WAIT2状态还有可能见到,可通过netstat命令查看。
  • CLOSE_WAIT:等待关闭,等待服务器调用close,若有大量该状态,说明未关闭socket ,处于半关闭状态,说明挥手只挥了一半
  • TIME_WAIT:当进入此状态,说明进程没有了,但连接还在,防止最后的ACK丢包,保证最后的ACK丢包后还有机会重传
    谁先尝试主动断开连接,谁就进入TIME_WAIT

当服务器端先断开连接时,我们立即再次启动服务器,会发现bind失败,因为当一个TCP连接处于TIME_WAIT状态时,我们无法立即使用该连接占用着的端口来建立一个新连接,为了解决这个问题,我们可以使用setsockopt()函数设置SO_REUSEADDR选项为1来重用这个端口号
在绑定端口号之前,加上下面的这段代码:

int opt=1;
setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

当有大量客户端和服务器进行连接时,并且每个连接的生存时间很短,但每秒都有大量的客户端来请求时,若都由服务器主动关闭连接,此时就会出现大量的TIME_WAIT连接,
当TIME_WAIT状态太多时,端口号可能不够用,

扫描二维码关注公众号,回复: 2314620 查看本文章

♥♥♥ 为什么客户端最后要等待2MSL?
这里写图片描述

MSL是TCP报文最大生存时间,一个数据从一端走到另一端所消耗的最长时间。这个时间在不同系统下可能不太一样,Centos7上默认为60秒,可通过cat /proc/sys/net/ipv4/tcp_fin_timeout来查看
其一,是为了防止最后的ACK丢失,还有机会重传
假设最坏情况下,客户端发给服务器的ACK确认报文在即将到达时丢失了,那么服务器在收不到ACK的情况下会重传FIN,客户端就能在这2MSL时间段内收到重传的FIN,然后自己会重传ACK,之后重启2MSL计时。
其二,是为了防止上面提到的“已失效的连接请求报文段”再次出现在本连接中。客户端发送完最后一个ACK后,再经过2MSL就可以使本连接产生的所有报文都从网络中消失,这样下一个新的连接中就不会出现这次旧的连接中

♥♥♥ 为什么连接的时候是三次握手,断开连接是四次挥手?

在建立连接时,服务器处于LISTEN状态,一旦收到来自客户端的连接请求SYN时,把ACK和SYN放在一个报文段发给对方。即双方各自向对方发送连接请求,并且双方各自向对方发送确认,一共是四次,只是其中有两次合二为一,所以是三次握手。
而关闭连接时,服务器收到客户端的FIN后,只表示对方对方不再发送数据了,但是还可以接收数据,自己也可能还有数据要发送给对方。ACK是内核收到FIN后直接发送的,而服务器的FIN是在自己无数据向客户端发送时,要用户手动调用close才会发送FIN,因此ACK和FIN无法合并

♥ 保活计时器的作用

当客户端和服务器建立了稳定的连接,而此时客户端突然出现了故障,那么服务器就不能收到客户发来的数据,那么,服务器不能一直等下去浪费资源。为此,TCP设置了一个保活计时器,服务器每收到一次客户端的请求都会重新复位这个计数器,时间通常为2小时,若2小时没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次,若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

服务器与客户端的状态变化

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/shidantong/article/details/81159597