TCP标志位
TCP标志位一共有六种,表示不同的请求目的(粗体常用).
- SYN (Synchronize): 发送同步标志,用于建立连接.
- ACK (Acknowledge): 发送确认标志,表示确认收到请求.
- FIN (Finish): 发送结束标志,表示关闭一个 TCP 连接.
- PSH (push):发送推送操作,数据到达接收端后,不进入队列排队,尽可能让应用程序先处理.
- RST (reset): 发送充值标志,表示复位对应的 TCP 连接.
- URG (urgent): 发送紧急标志,用于保证 TCP 连接不被中断,并且督促中间层设备尽快处理.
TCP 序列号, 确认号
作用
序列号和确认号是 TCP 实现可靠传输的依赖, TCP 使用序列号来记录发送数据包的顺序.TCP 传送一个数据包后, 只有在指定时间里收到这个包的确认信息,才会将其从队列中删除,否则就会重新发送这个数据包.对接收方而言,通过分析序列号可以保证数据按照正确的顺序进行重组.
序列号 Sequence Number
当 SYN = 1 时, seq 即为当前连接的初始序列号(Initial Sequence Number,ISN).
当 SYN = 0 时, seq 为报文中第一个字节的序列号.
序列号的规则
握手阶段,[SYN] 包即使没有传送数据,也会消耗一个序列号.因此,建立连接后的序列号从[ISN+1] 开始.
挥手阶段,[FIN/ACK] 包即使没有传送数据,也会消耗掉一个序列号.
数据传输阶段,序列号 = 第一个报文段的序列号 + 已经发送的字节数.
-
比如第一个报文段的序列号为
[S],已经发送了 100 个字节,则下一个报文段的序列号为[S+100]. -
如果某个报文段不携带数据,不会消耗序列号,下一个报文段还是用相同的序列号发送.
-
正常情况下,B 给 A 的确认号, 就是 A 下一个报文段的序列号.
客户端三次握手第三步的 [ACK] 包,和传输阶段的第一个报文段有相同的序列号.
确认号 Acknowledge Number
ACK = 1 时才有效,表示接收方期待的下一个报文段的序列号.一般是上次收到的报文段的 seq + 1.
三次握手
三次握手过程
三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包.进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常,指定自己的初始化序列号为后面的可靠性传送做准备.实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。
第一次握手: 客户端请求建立连接, 向服务端发送一个同步报文 (SYN = 1), 同时选择一个随机数 x 作为初始序列数 (ISN),将自身状态修改为 SYN_SENT.
第二次握手: 服务端收到连接请求报文后,如果同意建立连接吗,则会发送一个同步确认报文 (SYN = 1,ACK = 1), 确认号为 x + 1, 选择一个随机数 y 作为初始序列数 (ISN), 将自身状态修改为 SYN_RCVD.
第三次握手: 客户端收到服务端的确认后,向服务端发送一个确认报文 (ACK= 1), 确认号为 y + 1, 序列号为 x + 1.将自身状态修改为 ESTABLISHED, 服务端收到确认消息时,也会将自身状态修改为 ESTABLISHED.
这时就完成了三次握手,客户端和服务端分别会以 [x + 1] 和 [y + 1] 的序列号开始传输数据.
为什么是三次握手,而不是两次握手
三次握手是为了双方明确自身和对方的收发能力是否有问题.
第一次握手: 服务端收到消息后,知道了自己的接收能力,客户端的发送能力没有问题.
第二次握手: 客户端收到消息后,知道了自己的发送,接收能力,服务端的发送,接收能力没有问题.
第三次握手: 服务端收到消息后,知道了自己的发送能力,客户端的接收能力没有问题.
第三次握手失败怎么办
当第三次握手失败时,服务器并不会重传ack报文,而是直接发送[RST]报文段,进入 CLOSED 状态.这样做的目的是为了防止 SYN 洪泛攻击.
四次挥手
四次挥手过程
建立一个连接需要三次握手,而终止一个连接要经过四次挥手.这由 TCP 的半关闭造成.所谓的半关闭,其实就是 TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力.
TCP 连接的拆除需要发送四个包,因此称为四次挥手,客户端或服务端均可主动发起挥手动作.
第一次挥手: 客户端发送一个结束报文 (FIN = 1),同时选择一个随机数 x 作为序列号,将自身状态修改为 FIN_WAIT1,服务端收到请求后,将自身状态修改为 CLOSE_WAIT.
第二次挥手: 服务端发送一个确认报文 (ACK =1), 同时选择一个随机数 y 作为序列号, 确认号为 x + 1,客户端收到请求后,会将自身状态修改为 FIN_WAIT2.
第三次挥手: 当服务端可以关闭连接时,发送一个结束确认报文 (FIN = 1,ACK = 1), 同时选择一个随机数 z 作为序列号, 确认号为 x + 1,并将自身的状态修改为 LAST_ACK.
第四次挥手: 当客户端收到结束请求时,会将自身状态修改为 TIME_WAIT,将在 2 MSL 时间后进入 CLOSED 状态. 同时会发送确认报文 (ACK = 1),序列号为 x + 1, 确认号为 z + 1.服务端收到确认消息后,进入 CLOSED 状态.
挥手为什么要四次
当连接处于半关闭状态时,TCP 是允许单向传输数据的.为便于理解,我们把先关闭连接的一方叫做主动方,后关闭连接的一方叫做被动方.当主动方关闭连接时,被动方仍然可以在不调用 close 函数的状态下,长时间发送数据,此时连接处于半关闭状态.这一特性是 TCP 的双向通道互相独立所致,却也使得关闭连接必须通过四次挥手才能做到.
2MSL等待状态
TIME_WAIT 状态也称为2MSL等待状态.每个具体 TCP 实现必须选择一个报文段最大生存时间 MSL(Maximum Segment Lifetime),它是任何报文段被丢弃前在网络内的最长时间.这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段.
对一个具体实现所给定的 MSL 值,处理的原则是:当TCP执行一个主动关闭发回最后一个ACK之后,该连接必须在 TIME_WAIT 状态停留的时间为2倍的 MSL.这样可让 TCP 在最后的 ACK 丢失时重新发送 FIN.
这种 2MSL 等待的另一个结果是这个 TCP 连接在 2MSL 等待期间,定义这个连接的 Socket (客户的IP地址和端口号,服务器的IP地址和端口号)不能再被使用.这个连接只能在 2MSL 结束后才能再被使用.
四次挥手最后 ACK 时等待 2MSL 的意义
保证客户端发送的最后一个ACK报文段能够到达服务端
最后一个 ACK 报文可能会丢失, 此报文丢失会导致处于 LAST_ACK 状态的服务端无法收到对 FIN-ACK 报文的确认,服务端会超时重传.而超时重传和客户端收到 FIN-ACK 报文时发送 ACK 消息的最大耗时相等.客户端在收到 FIN-ACK 消息时会重置 TIME_WAIT 的时间.
防止"已失效的连接请求报文段"出现在本连接中
客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段.
如果连接已建立,客户端出现故障怎么办
TCP 设有一个保活计时器.服务器每收到一次客户端的数据,都会重新复位这个计时器,时间通常是设置为 2 小时.若 2 小时还没有收到客户端的任何数据,服务器就开始重试:每隔 75 分钟发送一个探测报文段,若一连发送 10 个探测报文后客户端依然没有回应,那么服务器就认为连接已经断开了.