一、TCP简介及报文格式
1、简介
TCP提供面向连接、可靠的字节流服务。
2、报文格式
重点字段:
- 序号:Seq序号,32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
- 确认序号:ACK序号,32位,只有ACK标志位为1时,确认序号字段才有效,Ack = Seq + 1。
- 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等。
标志位 | 具体含义 |
---|---|
URG | 紧急指针(urgent pointer)有效 |
ACK | 确认序号有效 |
PSH | 接收方应尽快将报文交给应用层 |
RST | 重置连接 |
SYN | 发起一个新连接 |
FIN | 释放一个连接 |
- 注意
- (a)不要将确认序号Ack与标志位ACK搞混;
- (b)确认方Ack = 发起方Req + 1,两端配对。
二、三次握手(Three-way Handshake)
1、含义
建立TCP连接时,客户端和服务器总共发送3个包。
2、目的
连接服务器指定窗口,建立TCP连接,并同步连接双方序列号和确认号并交换。TCP窗口大小信息在socket编程中,客户端执行connect()时,将触发三次握手。
-
注意:下图要达到熟练默写程度。
3、过程
- 1st 握手 - SYN 报文:
SYN=1,seq=x
客户端发送一个TCP的SYN标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序列号X,保存至包头的序列号(Sequence Number)字段里。
- 2nd 握手 - SYNACK 报文:
SYN=1, ACK=1, seq=y, ACKnum=x+1
服务器发回确认包(ACK)应答,即SYN标志位均为1,同时将确认序号(Acknowledgment Number)设置为客户的ISN+1,即x+1.
- 3rd 握手 - ACK 报文:
ACK=1,ACKnum=y+1
客户端再次发送确认包(ACK),SYN标志位为0,ACK标志位为1,并把服务器发来ACK序号字段+1,放在确定字段中发送给对方,并在数据段放写的ISN+1.
4、SYN攻击
在三次握手过程中,服务器发送SYN-ACK
之后,收到客户端ACK
之前的TCP连接
称为半连接(half-open connect
)。此时服务器处于SYN-RECV
。当收到ACK后,服务器转入ESTABLISHED
状态。
SYN攻击就是攻击客户端,在短时间内伪造大量不存在的IP地址,向服务器不断发送SYN包,服务器回复确认包,并等待客户确认。 由于源地址不存在,服务器需要不断重发直至超时,这些伪造SYN包将长时间占用未连接队列,正常SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪
。
SYN攻击是典型的DDOS攻击,检测SYN攻击非常方便,当你在服务器上看到大量半连接状态时,特别是IP地址是随机的,基本可用断定这是一次SYN攻击,在Linux上可用如下命令检测是否被SYN攻击:
netstat -n -p TCP | grep SYN_RECV
一般较新的TCP/IP协议都会对这一过程进行修正来防范SYN攻击,修改TCP协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等,但不能完全防范。
追问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又发出一次新的连接请求,于是A就发出确认报文段,同时建立连接。
-
由于现在A并没有发出建立连接请求,因此不理睬B的
SYNACK报文
,也不会向B发送数据,但B却以为新连接已经建立,并一直等待A发来的数据,B的许多资源被白白浪费。
追问2:三次握手过程中可以携带数据吗?
前两次不行,第三次可以携带。
追问3:为什么?
假如第一次可以,如果有人恶意攻击服务器,那他在第一次SYN 报文
中放入大量数据。因为攻击者根本不理会服务器的接收、发送能力是否正常,只是疯狂重复发 SYN 报文,这会让服务器花费很多内存与时间来接收这些报文。
也就是说,第一次握手不能放数据,1个简单原因服务器会更容易受到攻击。而对于第三次,此时客户端处于 ESTABLISHED
状态,已经建立起连接,知道服务器接收与发送能力正常,所以携带数据也没毛病。
追问4:ISN(Initial Sequence Number)是固定的吗?
不固定,client_isn
是随机生成的,而server_isn
则需要根据SYN报文中的源、IP和端口,加上服务器本身密码数进行相同散列得到,显然也不固定。
追问4:第三次握手失败了怎么办?
- 在第2次握手中,
server
向client
发送SYN+ACK报文
后,就会启动一个定时器,等待client
返回的ACK报文。 - 如果第三次失败,
client
给server
返回了ACK报文
,server
并不能收到这个ACK报文
。那server
就会启动超时重传机制,超过规定时间会重新发起第2次握手,向client
发送SYNACK
。重传次数默认5次。 - 如果到重传指定次数,仍未收到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:服务器为什么使用特殊的初始序号server_isn?
服务器使用特定初始序列号server_isn(从源和目的地IP和端口的散列中获取) 可以用来抵御SYN洪水攻击
。
三、 四次挥手(four-way handshake)
1、含义
TCP
连接拆除需要发送四个包,因此称为四次挥手.
Client或Server
均可主动发起。在socket
编程中,执行close()
操作即可产生挥手操作。
2、过程
- 1st: 先由
Client
向Server
发送 1 个FIN报文
,用来关闭Client到Server
的数据传送; - 2nd: 当
Server收到Client
的FIN时,会回复一个ACK,其中ACK值=FIN+SEQ
; - 3rd: 然后
Server向Client
发送一个FIN,用来关闭Server到Client
的数据传送; - 4th: 当
Client收到Server
的FIN时,回复 1 个ACK给Server,其中ACK值=FIN+SEQ
。
上面是一方主动关闭,另一方被动关闭情况,实际还会出现同时发起主动关闭的情况,具体如下:
追问1:为什么建立连接是三次握手,而关闭连接却是四次挥手呢?(*2)
关键在中间两步:
- 这是因为服务端在
LISTEN
状态下,收到建立连接请求的SYN报文
后,把ACK和SYN放在一个报文里发送给客户端。 - 而关闭连接时,收到对方的FIN报文时,仅表示对方不再发送,但还能接收数据,己方也未必全部发送完毕。
所以只能先回复一个ACK报文,告诉客户端“你发的FIN报文已收到”。等服务器所有报文发送/接收完,才能发送FIN报文。因此ACK和SYN分开发送,要四次握手。
追问1:为什么会有第二次、第三次、第四次挥手?(*2)
确保数据能完整传输。
- 当服务器收到客户端的FIN报文通知时,它仅仅表示客户端没有数据再发送给服务器了。
- 当服务器未将所有数据都发给了主动方,所以服务器不会马上关闭SOCKET,可能还需要发完数据后,再发FIN给主动方,表示同意关闭连接。
所以这里ACK报文和FIN报文大多数情况下是分开发送。
追问2:四次挥手释放连接时,等待2MSL的意义?(*2)
-
保证客户端发送的最后1个ACK报文能到达服务器。假设网络不可靠,ACK报文丢失。如果服务端发出FIN报文后没收到ACK报文,就会重发FIN报文(收到不再发),此时处于TIME-WAIT的客户端就会重发ACK报文。
当然,客户端也不能无限等待这个FIN报文,需要设置一个定时器,2MSL正好,因为1个最大发送和1个回复最长时间没收到ACK,可以推断ACK报文已经被服务器接收,所以结束TCP连接。 -
防止已失效的连接请求报文段出现在新连接中。客户端发完最后1个ACK报文后,再经过2MSL时间,就可以使网络不通畅产生的滞留报文段失效,这样下一个新连接中就不会出现旧的连接请求报文。
补充:
MSL—Maximum Segment Lifetime,指报文在网络中最大存活时间,2MSL就是1个发送和1个回复所需最大时间。
追问3:为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSE状态?
理论上,四个报文都发送完毕,就可以直接进入CLOSE状态。但网络如果不可靠,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。
追问4:四次挥手过程中服务端的哪几种状态,哪几种包?
……
四、参考
1、TCP的三次握手四次挥手
2、阿里面试: HTTP、HTTPS、TCP/IP、三次握手四次挥手过程?(附全网最具深度讲解)
3、(转)TCP为什么需要3次握手与4次挥手
4、TCP三次握手/四次挥手 及 状态变迁图
5、面试官,不要再问我三次握手和四次挥手
6、“三次握手,四次挥手”你真的懂吗?
7、TCP为什么需要第三次握手?第三次握手失败了会怎么样?