NOTE本篇是关于 TCP 的知识点记录,会比较琐碎。
TCP(Transmission Control Protocol):传输控制协议是一种面向连接的、可靠的、基于字节流的传输层通信协议;是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议;TCP 位于网络 OSI 七层模型中的第四层 Transport(传输)层。
一个 TCP 连接需要四元组来表示同一个连接,即源 IP(src_ip
)、源端口(src_port
)、目标 IP(dst_ip
)、目标端口(dst_port
);但其实还有一个协议,即五元组,而若仅考虑 TCP,则是四元组。
关键参数:
Sequence Number
:seq 序号,32 位,用来标识从 TCP 源端向目的端发送的字节流,发起方发送数据时对此进行标记,用于解决网络包乱序(reordering)问题Acknowledgement Number
:ack
确认序号,32 位,当 ACK
标志位为 1 时确认序号才有效(ack = seq + 1
),可用于解决丢包的问题,同时也表达了接收端希望接收的下一个数据包的起始 seq
序号Window
:又称 Advertised-Window
,即滑动窗口(Sliding Window),用于解决流控TCP Flag
:数据包(报文段)的类型,用于操控 TCP 有限状态机,关键标志位如下:URG
:紧急指针(urgent pointer),置为 1 表示启用了优先数据传输功能,数据不会存入缓冲区,而是直接发送给目标应用程序(即使缓冲区有数据)ACK
:确认序号有效(注意与确认序号 ack
的区别,仅当标志位的 ACK=1
时,确认序号 ack
才有效,连接建立后所有报文都需要将该标志位 ACK
置 1)PSH
:表示消息应当立即被「推送」,当标志位置为 1 时,PSH
报文段会放在缓冲区的末尾,然后立马将数据交付给目标应用程序,而无需等到缓冲区填满,使用场景:telnet、FTP(文件传输)RST
:重建连接SYN
:发起一个新连接FIN
:释放一个连接NOTE下文标志位全部大写表示,非标志位则小写表示
在网络当中的通信模式有以下两种:
概念:三次握手(Three-Way-Handshake)即建立 TCP 连接,一个 TCP 连接的创建需要客户端和服务端总共发送 3 个包才能完成。
SYN
置为 1,随机产生一个值 seq=x
,并将该数据包发送给 Server,Client 进入 SYN_SENT
状态,等待 Server 确认SYN=1
知道 Client 请求建立连接,Server 将标志位 SYN
和 ACK
都置为 1,ack=x+1
,随机产生一个值 seq=y
,并将该数据包发送给 Client 以确认连接请求,Server 进入 SYN_RCVD
状态ack
是否为 x+1,ACK
是否为 1,如果正确则将标志位 ACK
置为 1,ack=y+1
,并将该数据包发送给 Server,然后将状态修改为 ESTABLISHED
ack
是否为 y+1,ACK
是否为 1,如果正确则连接建立成功,Server 进入 ESTABLISHED
状态,完成三次握手,随后 Client 与 Server 即可开始传输数据三次握手的目的:主要是为了初始化 seq
的初始值,通信双方需要互相通知对方自己初始化的 seq
(即 ISN - Inital Sequence Number
),所以叫 SYN - Synchronize Sequence Numbers
(注意:这里的 SYN 不是指标志位的 SYN
,而是字面意思的同步序号),即握手流程中的序号 x 和 y,这两个序号会作为以后数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP 会用这个序号来拼接数据)。
半连接队列(SYN queue):在三次握手过程中,Server 发送 SYN - ACK
之后,收到 Client 的 ACK
之前的 TCP 连接称为半连接(half-open connect),此时 Server 处于 SYN_RCVD
状态,这些连接会存放在半连接队列中。
全连接队列(accepet queue):第三次握手中 Server 收到 Client 的 ACK
后,内核会把连接从 SYN queue(半连接队列)移除,然后创建新的全连接,并添加到 accept queue(全连接队列),等待进程调用 accept 函数时把连接取出来。
SYN 攻击:攻击者短时间伪造不同 IP 地址的 SYN
报文,服务端每接收到一个 SYN
报文,就进入 SYN_RCVD
状态,但服务端发送出去的 ACK + SYN
报文,无法得到未知 IP 主机的 ACK
应答,久而久之就会占满服务端的半连接队列,使得服务器不能为正常用户服务。
关于 SYN 攻击防御可以看看 SYN 攻击与避免方式,关键参数是 tcp_syncookies
,本意是当 SYN queue 满后 TCP 会通过 src_port、dest_port
和时间戳构建一个特殊的 seq
(又叫 cookie
)返回给 Client(第二次握手时),若是攻击者则不会有响应,若是正常连接则会携带该 cookie
,Server 只要验证该 cookie
即可建立连接(即使该连接不存在于 SYN queue)。
概念:四次挥手(Four-Way Wavehand)即终止 TCP 连接,就是指断开一个 TCP 连接时,需要客户端和服务端总共发送 4 个包以确认连接的断开。
FIN
,用来关闭 Client 到 Server 的数据传送,Client 进入 FIN_WAIT_1
状态FIN
后,发送一个 ACK
给 Client,确认序号 ack
为收到的 seq+1
(与 SYN
相同,一个 FIN
占用一个序号),Server 进入 CLOSE_WAIT
状态,Client 收到 ack
后进入 FIN_WAIT_2
状态FIN
,用来关闭 Server 到 Client 的数据传送,Server 进入 LAST_ACK
状态FIN
后,Client 进入 TIME_WAIT
状态,接着发送一个 ACK
给 Server,确认序号为收到的 seq+1
,Server 进入 CLOSED
状态,完成四次挥手FIN
,并进入 FIN_WAIT_1
状态,双方收到 FIN
后转为 CLOSING
状态ACK
,双方收到后进入 TIME_WAIT
状态从双方同时发起关闭操作的角度看,关闭连接就只有两次挥手,因为 TCP 是全双工协议,而且 Client 与 Server 两端都需要 FIN
和 ACK
,只不过有一方是被动的,所以看起来就是四次挥手,若两边同时断开连接,那就都会进入 CLOSING
状态,进而变成 TIME_WAIT
状态。
在 TCP 协议下,有限状态机(Finite State Machine)用于描述连接的「生命阶段(Life Stages)」。一个 TCP 设备与另一个 TCP 设备之间的每个连接都以没有连接的空(null)状态开始,然后经过一系列状态,直到建立连接。接着它保持在该(连接)状态,直到发生某些事情导致连接再次关闭,此时它继续通过另一系列过渡状态并返回到关闭状态。
以下三个缩写用于控制状态之间转换的三种类型的消息,对应于设置的 TCP 头标志位,以表示消息正在服务该功能:
SYN
:同步消息,用于启动和建立连接,如此命名是因为它的功能之一是在设备之间同步序列号(seq
)FIN
:完成消息,它是一个设置了 FIN
位的 TCP 段(Segment),表示设备要终止连接ACK
:确认消息,表示收到一条消息,例如一个 SYN
或一个 FIN
TCB(Transfer control block)
:传输控制块,用于存储有关连接的重要信息,如两个套接字号和指向发送-接收缓存区的指针,TCB 也用于实现滑动窗口机制,其保存跟踪接收和确认的字节数、接收和尚未确认的字节数、当前窗口大小等的变量,每个 TCP 连接都有自己的 TCB。
Active OPEN
:主动/活动 OPEN,使用 TCP 的客户端进程即为主动方,并通过实际发送 TCP 消息来启动连接(SYN 消息)Passive OPEN
:被动 OPEN,表示服务端进程正在侦听,等待客户端通过指定端口与其建立连接CLOSED
状态时CLOSED
状态时销毁状态 | 状态描述 | 事件与过渡 |
---|---|---|
CLOSED | 每个连接在建立过程开始之前开始的默认状态,在标准中也称为「虚构(fictional)」,因为该状态表示设备之间没有连接,要么尚未创建,要么刚刚被销毁 | Passive OPEN:服务器通过在 TCP 端口上进行被动打开来开始连接设置过程。同时,它建立了管理连接所需的数据结构(传输控制块或TCB),然后它转换到 LISTEN 状态Active OPEN,发送 SYN:客户端通过发送 SYN 消息开始连接建立,并为此连接建立 TCB。然后它转换到 SYN-SENT 状态 |
LISTEN | 设备(通常是服务器)正在等待接收来自客户端的同步(SYN )消息,它还没有发送自己的 SYN 消息 | 接收客户端 SYN,发送 SYN+ACK:服务器设备接收来自客户端的 SYN ,它发回一条包含自己的 SYN 的消息,并确认它收到的消息(ACK ),服务器进入 SYN-RECEIVED 状态 |
SYN-SENT | 客户端已发送同步(SYN)消息,并正在等待来自其他设备(通常是服务器)的匹配 SYN | 接收 SYN,发送 ACK:如果发送了 SYN 消息的设备从其他设备接收到 SYN ,但没有收到自己 SYN 的 ACK ,它会确认收到的 SYN ,然后转换到 SYN-RECEIVED 以等待确认它的 SYN 接收 SYN+ACK,发送 ACK:如果发送 SYN 的设备同时收到对其 SYN 的确认以及来自其他设备的 SYN ,它会确认收到的 SYN ,然后直接进入 ESTABLISHED 状态 |
SYN-RECEIVED | 设备既收到了来自其伙伴的 SYN (连接请求),又发送了自己的 SYN ,它现在正在等待对其 SYN 的 ACK 以完成连接设置 | 接收 ACK:当设备接收到它发送的 SYN 的 ACK 时,它会转换到 ESTABLISHED 状态 |
ESTABLISHED | 打开的 TCP 连接的「稳定状态(steady state)」,一旦连接中的两个设备都进入此状态,就可以自由交换数据,这将一直持续到连接因某种原因关闭 | 关闭、发送 FIN:设备可以通过发送带有 FIN (完成)位的消息来关闭连接,并转换到 FIN-WAIT-1 状态接收 FIN:设备可能会收到来自其连接设备的 FIN 消息,要求关闭连接,它将确认此消息并转换到 CLOSE-WAIT 状态 |
CLOSE-WAIT | 设备已收到来自其他设备的关闭请求(FIN ),现在必须等待本地设备上的应用程序确认此请求并生成匹配请求 | 关闭、发送 FIN:使用 TCP 的应用程序在被告知其他进程想要关闭后,向正在运行它的机器上的 TCP 层发送关闭请求,然后 TCP 向已经要求终止连接的远程设备发送一个 FIN ,然后转换到 LAST-ACK |
LAST-ACK | 已收到关闭请求并确认的设备已发送自己的 FIN 并正在等待此请求的 ACK | 为 FIN 接收 ACK:设备收到其关闭请求(FIN )的确认,并收到了另一个设备的 FIN 并确认了它,进入 CLOSED 状态 |
FIN-WAIT-1 | 处于此状态的设备正在等待其发送的 FIN 的 ACK ,或者正在等待来自其他设备的连接终止请求 | 为 FIN 接收 ACK:设备收到其关闭请求(FIN )的确认,转换到 FIN-WAIT-2 状态接收 FIN,发送 ACK:设备不接收自己 FIN 的 ACK ,而是接收来自其他设备的 FIN ,它确认该 FIN ,并进入 CLOSING 状态 |
FIN-WAIT-2 | 处于此状态的设备已收到终止连接请求的 ACK ,现在正在等待来自其它设备的匹配 FIN | 接收 FIN、发送 ACK:设备从其它设备接收 FIN ,确认后进入 TIME-WAIT 状态 |
CLOSING | 该设备已收到来自其他设备的 FIN 并为其发送了 ACK ,但尚未收到自己的 FIN 消息的 ACK | 为 FIN 接收 ACK:设备收到其关闭请求(FIN )的确认,转换到 TIME-WAIT 状态 |
TIME-WAIT | 该设备现在已经从另一个设备接收到一个 FIN 并对其进行确认,并发送了自己的 FIN 并收到了一个 ACK ,除了等待确保收到 ACK 并防止与新连接的潜在重叠(关于该状态的详细请参阅 TCP 连接终止) | Timer Expiration:在指定的等待期(2MSL)后,设备转换到 CLOSED 状态 |
相关概念:
MSL(Maximum Segment Lifetime)
:是构成 TCP 协议一部分的数据报的任何段在被丢弃之前在网络上保持有效的最大时间,默认是 2 分钟,此时间限制最终受用于传输 TCP 段的 IP 数据报中的 TTL 字段的限制MTU(Maximum Transmission Unit)
:最大传输单元,一个网络包(帧)的最大长度,即发送方能接收的有效负载,以太网中一般为 1500 字节,超过的话在进入到路由器时会被拒绝转发MSS(Maximum Segment Size)
:最大报文长度,除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度,若 HTTP 请求数据过长,超过 MSS 长度,则 TCP 会把数据拆成多个的数据包发送,而不是一次性发送所有数据,也就是拆包TCP.SO_REUSEADDR
:该参数用于设置当一个端口处于 TIME_WAIT
状态时,不用等待时间结束就可以直接进入使用模式TIME-WAIT
状态的持续时间为 ,也就是默认 4 分钟,TCP 连接中的一个套接字被干净地关闭将保持 TIME_WAIT
状态大约 4 分钟。如果许多连接被快速打开和关闭,则 TIME_WAIT
中的套接字可能会开始在系统上累积,若累积过多,会导致本地可用端口缺失而无法建立新连接。
防止来自一个连接的延迟段被误解为后续连接的一部分,当连接处于 等待状态时到达的任何段都将被丢弃
TIME_WAIT
中保持足够长的时间以确保来自先前连接的所有段都已失效,那么延迟段(具有适当的序列号)可能被误认为是第二个连接的一部分TIME_WAIT
将阻止新连接的数据损坏可靠地实现 TCP 的全双工连接终止:假设客户端先发起 FIN
,那么在服务端发送 FIN
后,没有收到 ACK
,就会导致服务端重发 FIN
,若没有 TIME-WAIT
,让连接直接进入 CLOSED
状态,那么会导致服务端收到的响应是 RST
,这将导致服务端发生错误,即使数据正确传输
超时重传指经过 RTO 时间后未收到接收方的 ACK 便重传数据报文。
RTT(Round Trip Time)
:即一个数据包从发出去到回来的时间。RTO(Retransmission TimeOut)
:超过 RTO 设置的时间没有收到 ack,数据包就会重发,通过 RTT 来控制 RTO 时间。存在的问题:
快速重传机制不以时间驱动,而是以数据驱动,基于接收端的反馈信息(ACK
)来引发重传。
假设发送 6 个报文,seq
分别是 1~6,其中只有 seq=3
未收到 ACK
,那么接收方后续收到 seq=4/5/6
时,ACK
也会是 3,发送方便可得知 seq=3
的报文需要重传。
存在的问题:ACK
只向发送端告知最大的有序报文段,但具体丢失的报文段是哪个并不清楚,比如上述例子,可能丢失的是 seq=3
,也可能是 seq=4/5/6
都丢失了
SACK(Selective ACK)是带选择确认的重传,在快速重传的基础上,接收端返回最近收到的报文段的序列号范围,发送端便知道该重传哪些报文段。
D-SACK(Duplicate SACK)在 SACK 的基础上作扩展,用于告诉发送方,接受方自己哪些数据重复接收了。
滑动窗口(Sliding Window)
:TCP 头里有个字段叫 Window
(Advertised-Window
),这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据,于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来,也就是流控。
拥塞处理(Congestion Handling)
:就是在网络中发生拥塞时,减少向网络中发送数据的速度,防止造成恶性循环;同时在网络空闲时,提高发送数据的速度,最大限度地利用网络资源。
网络拥塞
:网络中的数据太多,导致某个路由器处理不过来或处理的太慢。路由器的内存是有限的,若同一时间到达某个路由器的数据太多,这个路由器将无法接收所有的数据,只能将一部分丢弃;或者同一台路由器数据太多,后面到达的数据将要等待较长的时间才会被转发。
拥塞控制的四个算法: