三次握手和四次挥手

1. TCP简介及报文格式

1.1 简介

TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议。

1.2 报文格式

三次握手和四次挥手

重要字段:

  1. 端口号:16位,用来标识同一台计算机的不同的应用进程。
  • 1)源端口:源端口和IP地址的作用是标识报文的返回地址。

  • 2)目的端口:端口指明接收方计算机上的应用程序接口。

  1. 序号:Seq序号,32位,一次TCP通信 (从TCP连接建立到断开) 过程中某一个传输方向上的字节流的每个字节的编号。

  2. 确认序号:ACK序号,32位,用作对另一方发送来的TCP报文段的响应。只有ACK标志位为1时,确认序号字段才有效,其值是收到的TCP报文段的序号值加1。

  3. 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN 等。

    标志位 名称 具体含义
    URG 紧急标志 紧急指针(urgent pointer)有效
    ACK 确认标志 确认序号有效
    PSH 推标志 接收方应尽快将报文交给应用层
    RST 复位标志 重置连接
    SYN 同步标志 发起一个新连接
    FIN 结束标志 释放一个连接

2. 三次握手(Three-way Handshake)

2.1 含义

建立TCP连接时,客户端和服务器总共发送3个包。

2.2 三次握手过程

三次握手和四次挥手
  1. 客户端发送syn报文。SYN标志为1,指明客户端打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
  2. 服务器发回确认syn+ack报文。SYN标志为1,确认序号(Acknowledgement Number)为客户端的序列号加1,即X+1,并设置发送序号为Y。
  3. 客户端再次发送ack报文。 SYN标志位为0,确认序号(Acknowledgement Number)为服务器的序列号加1,即Y+1,并设置发送序号为Z。

问题1:为什么两次握手不行?

三次握手是为了让双方验证各自接收能力和发送能力。

  • 1st:A发送SYN给B,B接收到。这里B能确认A的发送能力和B的接收能力。

  • 2nd:B发送SYNACK给A,A收到。这里A能确认A的接收能力和B的发送能力。此外,A收到SYNACK,说明前面A的SYN成功到达B,也能确认A自己的发送能力和B的接收能力。至此,A已经确认双方各自发送能力和接收能力OK,因此转为ESTABLISHED状态。

  • 3rd:A发送ACK到B,B接收。这里B能确认A的发送能力和B的接收能力。由于B能收到ACK,说明前面发送的SYNACK已经被接受了,说明A的接收能力和B的发送能力正常。

若使用两次握手,就不能确认上述四种能力,可能有问题。

  • 假定A发的SYN报文没消失,而是在某网络节点长时间滞留了,以至于到连接释放后的某个时间才到达B。

  • 本来这是一个早已失效的报文段,但B收到此失效连接请求报文段后,却误以为是A又发出一次新的连接请求,于是B就发出确认报文段,同时建立连接。

  • 由于现在A并没有发出建立连接请求,因此不理睬B的SYNACK报文,也不会向B发送数据,但B却以为新连接已经建立,并一直等待A发来的数据,B的许多资源被白白浪费。

问题2:三次握手过程中可以携带数据吗?

前两次不行,第三次可以携带。 假如第一次可以,如果有人恶意攻击服务器,那他在第一次SYN 报文中放入大量数据。因为攻击者根本不理会服务器的接收、发送能力是否正常,只是疯狂重复发 SYN 报文,这会让服务器花费很多内存与时间来接收这些报文。也就是说,第一次握手不能放数据,1个简单原因服务器会更容易受到攻击。而对于第三次,此时客户端处于 ESTABLISHED 状态,已经建立起连接,知道服务器接收与发送能力正常,所以携带数据也没毛病。

问题3:ISN(Initial Sequence Number)是固定的吗?

不固定,client_isn是随机生成的,而server_isn则需要根据SYN报文中的源、IP和端口,加上服务器本身密码数进行相同散列得到,显然也不固定。

问题4:第三次握手失败了怎么办?

  1. 在第2次握手中,server向client发送SYN+ACK报文后,就会启动一个定时器,等待client返回的ACK报文。
  2. 如果第三次失败,client给server返回了ACK报文,server并不能收到这个ACK报文。那server就会启动超时重传机制,超过规定时间会重新发起第2次握手,向client发送SYNACK。重传次数默认5次。
  3. 如果到重传指定次数,仍未收到ACK应答,那一段时间后server会关闭这个连接。但client认为这个连接已建立,如果它向server写数据,server将回应RST包、强制关闭TCP连接,以防止SYN攻击。

问题5:什么是SYN攻击?如何防范?

在三次握手过程中,服务器发送SYN-ACK之后,收到客户端ACK之前的TCP连接称为半连接(half-open connect)。此时服务器处于SYN-RECV。当收到ACK后,服务器转入ESTABLISHED状态。

SYN攻击就是攻击客户端,在短时间内伪造大量不存在的IP地址,向服务器不断发送SYN包,服务器回复确认包,并等待客户确认。 由于源地址不存在,服务器需要不断重发直至超时,这些伪造SYN包将长时间占用未连接队列,正常SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

SYN攻击是一种典型的DoSe/DDoS攻击。

问题6:如何检测SYN攻击?

检测SYN攻击非常方便,当你在服务器上看到大量半连接状态时,特别是IP地址是随机的,基本可以断定这是一次SYN攻击,在Linux/Unix可用netstat命令检测。

问题7:如何防御SYN攻击?

SYN攻击不能完全被阻止,除非重新设计TCP协议。能做的就是就是尽可能减轻SYN攻击危害,常见防御方法有:缩短超时(SYN Timeout)时间、增大最大半连接数、过滤网关防护、SYN cookies技术。

问题8:握手过程中除了序号的同步,还会同步什么信息?

  • 1)序号;
  • 2)标志位:SYN(发起一个连接)、ACK(确认序号有效);
  • 3)窗口(流量控制中的接收窗口)。

问题9:通过什么方式去知道某台电脑上还能建立多少个TCP连接?

系统用四元组{Local IP,Local Port,Remote IP,Remote Port} 来唯一标识TCP连接。

Client发起TCP连接时,系统通常会选取一个空闲本地端口(Local Port),该端口,类型是Unsigned short,最大65536,端口0有特殊含义,因此最大可用端口,即最大TCP连接数只有65535。

Server部分的Remote IP和Remote Port可变,因此最大TCP连接为客户端IP数*客户端Port数,IPV4简单情况(不考虑地址分类) 最大连接数为232(IP数)* 216(Port数) ,也就是Server端单机最大TCP连接数约为248

3. 四次挥手(four-way handshake)

3.1 含义

TCP连接拆除需要发送四个包,因此称为四次挥手。

Client或Server均可主动发起。在socket编程中,执行close()操作即可产生挥手操作。

3.2 四次挥手过程

三次握手和四次挥手
  1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。

  2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

  3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。

  4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

3.3 深入理解TCP连接的释放

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。
简单说来是 “先关读,后关写”,一共需要四个阶段。以客户机发起关闭连接为例:

  1. 服务器读通道关闭
  2. 客户机写通道关闭
  3. 客户机读通道关闭
  4. 服务器写通道关闭

关闭行为是在发起方数据发送完毕之后,给对方发出一个FIN(finish)数据段。直到接收到对方发送的FIN,且对方收到了接收确认ACK之后,双方的数据通信完全结束,过程中每次接收都需要返回确认数据段ACK。

3.4 TCP状态迁移

三次握手和四次挥手

客户端TCP状态迁移:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

服务器TCP状态迁移:

CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED 

各个状态的意义如下:

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 - 没有任何连接状态;

SYN_RECV

服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接 。

CLOSE_WAIT

发起TCP连接关闭的一方称为client,被动关闭的一方称为server。在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。出现这种状况一般都是由于server端代码的问题,如果服务器出现大CLOSE_WAIT,应该要考虑检查代码 。

TIME_WAIT

根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用。

问题1:为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

关键在中间两步:

  • 这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
  • 而关闭连接时,收到对方的FIN报文时,仅表示对方不再发送,但还能接收数据,己方也未必全部发送完毕。所以只能先回复一个ACK报文,告诉客户端“你发的FIN报文已收到”。等服务器所有报文发送/接收完,才能发送FIN报文。因此ACK和SYN分开发送,要四次握手。

问题2:四次挥手释放连接时,等待2MSL的意义?

  1. 保证客户端发送的最后1个ACK报文能到达服务器。假设网络不可靠,ACK报文丢失。如果服务端发出FIN报文后没收到ACK报文,就会重发FIN报文(收到不再发),此时处于TIME-WAIT的客户端就会重发ACK报文。
    当然,客户端也不能无限等待这个FIN报文,需要设置一个定时器,2MSL正好,因为1个最大发送和1个回复最长时间没收到FIN,可以推断ACK报文已经被服务器接收,所以结束TCP连接。
  2. 防止已失效的连接请求报文段出现在新连接中。客户端发完最后1个ACK报文后,再经过2MSL时间,就可以使网络不通畅产生的滞留报文段失效,这样下一个新连接中就不会出现旧的连接请求报文。

问题3:四次挥手过程中服务端的哪几种状态,哪几种包?

  1. 收到FIN之前是ESTABLISHED状态;
  2. 发完ACK过程变为CLOSE_WAIT(关闭等待) 状态;
  3. 发完FIN报文后是LAST_ACK(最后确认) 状态;
  4. 收到客户端ACK报文后变为CLOSED状态。
上一篇:经受时延的确认(Delay ACK)


下一篇:RabbitMQ的消息可靠性投递