1. TCP简介及报文格式
1.1 简介
TCP(Transmission Control Protocol)传输控制协议是一种面向连接的、可靠的、基于字节流的传输层协议。
1.2 报文格式
重要字段:
- 端口号:16位,用来标识同一台计算机的不同的应用进程。
-
1)源端口:源端口和IP地址的作用是标识报文的返回地址。
-
2)目的端口:端口指明接收方计算机上的应用程序接口。
-
序号:Seq序号,32位,一次TCP通信 (从TCP连接建立到断开) 过程中某一个传输方向上的字节流的每个字节的编号。
-
确认序号:ACK序号,32位,用作对另一方发送来的TCP报文段的响应。只有ACK标志位为1时,确认序号字段才有效,其值是收到的TCP报文段的序号值加1。
-
标志位:共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 三次握手过程
- 客户端发送syn报文。SYN标志为1,指明客户端打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里。
- 服务器发回确认syn+ack报文。SYN标志为1,确认序号(Acknowledgement Number)为客户端的序列号加1,即X+1,并设置发送序号为Y。
- 客户端再次发送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:第三次握手失败了怎么办?
- 在第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:握手过程中除了序号的同步,还会同步什么信息?
- 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 四次挥手过程
-
客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
-
服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。
-
服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
-
客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。
3.3 深入理解TCP连接的释放
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
TCP协议的连接是全双工连接,一个TCP连接存在双向的读写通道。
简单说来是 “先关读,后关写”,一共需要四个阶段。以客户机发起关闭连接为例:
- 服务器读通道关闭
- 客户机写通道关闭
- 客户机读通道关闭
- 服务器写通道关闭
关闭行为是在发起方数据发送完毕之后,给对方发出一个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个ACK报文能到达服务器。假设网络不可靠,ACK报文丢失。如果服务端发出FIN报文后没收到ACK报文,就会重发FIN报文(收到不再发),此时处于TIME-WAIT的客户端就会重发ACK报文。
当然,客户端也不能无限等待这个FIN报文,需要设置一个定时器,2MSL正好,因为1个最大发送和1个回复最长时间没收到FIN,可以推断ACK报文已经被服务器接收,所以结束TCP连接。 - 防止已失效的连接请求报文段出现在新连接中。客户端发完最后1个ACK报文后,再经过2MSL时间,就可以使网络不通畅产生的滞留报文段失效,这样下一个新连接中就不会出现旧的连接请求报文。
问题3:四次挥手过程中服务端的哪几种状态,哪几种包?
- 收到FIN之前是ESTABLISHED状态;
- 发完ACK过程变为CLOSE_WAIT(关闭等待) 状态;
- 发完FIN报文后是LAST_ACK(最后确认) 状态;
- 收到客户端ACK报文后变为CLOSED状态。