目录
[TCP 的连接状态查看](#TCP 的连接状态查看)
TCP协议的特性
TCP 是面向一对一连接的、可靠的、基于字节流的传输层通信协议,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
1.面向一对一连接:
TCP一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
一个 TCP 连接一旦建立,其通信的两个端点就固定了,不能被其他无关的主机"借用"或"复用"来进行通信。虽然连接不必在每次数据传输完成后立即断开(通过持久连接可以复用),但它始终是专属该一对端点的。
- TCP 的"一对一": 一个建立起来的 TCP 连接,总是严格地定义在两个确定的端点之间 。一个端点由 IP 地址和端口号组成。所以,一个 TCP 连接是由一个 5 元组 唯一标识的:(源 IP, 源端口, 目的 IP, 目的端口, 协议)。数据只能在这两个确定的端点之间双向传输。
- UDP 的"一对多": UDP 是无连接的。一个主机上的一个 UDP socket(绑定一个源 IP 和源端口)可以独立地向多个不同的目的 IP 和目的端口发送数据报。这使得 UDP 可以更容易地实现广播(一对所有)和组播(一对多组)的功能,或者从一个源发送数据到多个不同的 unicast 目的地。
"是不是对于一个tcp连接来说,是不是只能用在一次通信中,也就是源主机和目的主机不能变动?"
- 正确。 对于一个已经建立起来的 TCP 连接,其源主机、源端口、目的主机、目的端口都是固定不变的,直到这个连接被断开。这个 5 元组确定了这个连接的唯一性。这里的"一次通信"如果理解为从连接建立到断开的整个会话过程,那么源主机和目的主机(以及端口)确实不能变动。
"这个tcp连接是不是不能被其他通信主机复用?"
- 非常正确。 一个特定的 TCP 连接,由它的 5 元组 唯一确定。这个连接是专属于建立它的那对端点(源 IP+端口 到 目的 IP+端口)的。
- 其他任何主机 (无论源 IP 或源端口不同)如果想与这个目的主机建立 TCP 通信,都必须建立自己的、全新的 TCP 连接,这个新连接会有不同的 5 元组(至少源 IP 或源端口会不同)。
- 这个连接也不能用来与其他的目的主机通信。 通过这个连接发送的数据只能到达建立连接时确定的那个目的主机和端口。
TCP 协议的核心理论是面向连接且是一对一的。这直接映射到网络编程中:
一个 TCP 套接字在成功建立连接后,就专属于这条特定的、一对一的连接(源与目的主机)。因此,在网络编程中,这个TCP 套接字不能复用来与其他不同的主机进行通信,需要另建套接字。
而 UDP 协议是无连接且支持一对多的。一个 UDP 套接字不绑定特定的连接,因此在网络编程中,这个UDP 套接字可以复用来与多个不同的主机进行通信。
2.可靠的:
无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
数据传输的过程中可能会遇到:
- 数据包丢失
- 数据包乱序
- 数据包重复
- 数据损坏
TCP 如何在面对这些问题时,仍然能够保证应用程序发送的整个字节流能够完整、正确、按发送顺序地到达接收端呢?它主要依赖于以下几个关键机制:
通过下面这些机制的协同工作,TCP 实现了"可靠性":
- 即使网络发生丢包、乱序、重复或数据损坏,TCP 都可以通过序号、确认应答、重传和错误检测来发现问题。
- 然后通过重传机制恢复丢失或损坏的数据。
- 最终在接收端重新排序 并去重,确保将一个完整的、正确无误的字节流按发送顺序提交给应用程序。
3.基于字节流的:
- 它对应用程序发送的数据没有"消息"或"数据报"的概念上的边界,只是一个连续的字节流。
- 为了保证这个字节流的有序性和完整性,TCP 在接收端会使用缓冲区对乱序到达的报文段进行排序和暂存,直到前面的数据都到达并按序排好,才一起提交给应用层。
- 同时,它会自动检测并丢弃在网络中重复出现的报文段,避免污染数据流。
TCP的报文格式介绍
序列号 (Sequence Number):在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一 次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
确认应答号 (Acknowledgement Number):指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序 号以前的数据都已经被正常接收。用来解决不丢包的问题。
TCP头部中的首部长度字段 一共有4位,这个字段的单位是 **4 字节。**4 位字段的最大值是 2^4-1=15。所以,TCP 头部的最大长度是 15×4 字节 = 60 字节。标准的、不含选项的 TCP 头部长度是固定的 20 字节。因此,TCP 头部最多可以包含 40 字节(60 - 20 = 40)的**选项(Options)**字段。
TCP 报文段的头部包含一系列重要的控制位 (或称标志位,Flags),它们是单个比特位(0 或 1),用于控制 TCP 连接的状态和行为。
-
URG (Urgent) - 紧急指针有效标志
- 作用: 当此标志位为 1 时,表示 TCP 头部中的 紧急指针(Urgent Pointer)字段是有效的。
- 用途: 理论上用于标记数据流中的紧急数据,提示接收方应用尽快处理这部分数据。
- 实际情况: 不同操作系统和网络设备对紧急数据的处理方式存在差异,导致其行为不可预测,现在已经基本不使用URG和紧急指针了,所以可以忽略。
-
ACK (Acknowledgement) - 确认标志
- 作用: 当此标志位为 1 时,表示 TCP 头部中的 确认号(Acknowledgement Number)字段是有效的 。确认号包含的值是发送此报文段一方期望接收的下一个按序字节的序号。
- 用途: 用于确认收到了对方发送的数据。在 TCP 连接建立后的所有报文段中,几乎都会设置 ACK 标志,表明这是一个确认报文。它是 TCP 可靠性 和流量控制的关键。
- 注意: 在三次握手的第一个 SYN 报文段中,ACK 标志通常是 0,因为它还没有收到任何数据需要确认。
-
PSH (Push) - 推送标志
- 作用: 当此标志位为 1 时,是发送方发出的一个请求,要求接收方的 TCP 协议栈立即将当前所有已接收的数据提交给接收应用程序,而不要等待接收缓冲区满或其他延迟提交的条件。
- 用途: 用于交互式应用或在数据传输结束时,确保数据尽快从发送方的用户空间推送到接收方的用户空间。
-
RST (Reset) - 复位标志
- 作用: 当此标志位为 1 时,表示连接出现了严重的错误,或者接收到了无效的报文段。它用于立即异常终止一个 TCP 连接。
- 用途: 发送 RST 报文通常表示:
- 收到了一个错误的报文段(如发送到了一个未开放的端口)。
- 一方希望立即中断一个连接,不进行正常的四次挥手。
- 特点: 发送 RST 的一方不会发送任何确认,接收到 RST 的一方会立即关闭连接,并且不会发送确认给发送方。
-
SYN (Synchronize) - 同步标志
- 作用: 当此标志位为 1 时,表示这是建立一个新连接的请求报文段。它用于同步通信双方的初始序号。
- 用途:
- 在 TCP 三次握手的第一个报文段中,客户端设置 SYN=1,并发送自己的初始序号 (ISN)。
- 在 TCP 三次握手的第二个报文段中(SYN-ACK),服务器设置 SYN=1 和 ACK=1,发送自己的初始序号,并确认客户端的序号。
-
FIN (Finish) - 结束标志
- 作用: 当此标志位为 1 时,表示发送方的数据已经发送完毕,希望关闭此方向的数据传输(即单方面关闭发送数据)。
- 用途: 用于启动 TCP 连接的正常关闭过程(四次挥手)。当一方完成数据发送后,会发送一个设置了 FIN=1 的报文段来关闭发送通道。连接的关闭是双向独立的,双方都需要发送 FIN 来完全关闭连接。
TCP 头部中的窗口大小字段是 TCP 协议实现流量控制的关键机制,后面我们讲流量控制的时候再详细介绍。
TCP头部中的检验和 是端到端进行的,中间路由器不进行校验。这既是因为路由器的工作层次和性能考虑,也是因为 IP 分片等机制会导致中间设备无法获得计算原始校验和所需的完整信息。校验和的验证必须在目的主机 IP 层完成重组后,由传输层来执行。
TCP报文中的可选选项详细介绍
TCP 报文头部有一个固定长度是 20 字节。在这个固定头部之后,有一个长度可变的可选选项字段,它的长度最长可达 40 字节,使得整个 TCP 报文头部最长可达 60 字节(固定 20 + 可选 40)。
选项字段的结构:
整个选项字段由一个或多个独立的**选项条目(Option Entry)**组成。每个选项条目通常具有以下结构:
+--------+--------+----------------+
| Kind | Length | Option Data |
+--------+--------+----------------+
1 byte 1 byte Variable bytes
- Kind (种类): 这是一个 1 字节的字段,表示选项的类型。一些 Kind 值是标准定义的,用于表示特定的选项。
- Length (长度): 这是一个 1 字节的字段,表示整个选项条目 的总长度(包括 Kind、Length 和 Option Data 本身)的字节数。注意: 有两个特殊的选项(Kind 0 和 Kind 1)的长度是固定的 1 字节,它们没有独立的 Length 字段。
- Option Data (选项数据): 这是一个长度可变的字段,包含该选项的具体参数或信息。
填充 (Padding):
TCP 头部总长度(包括选项字段)必须是 4 字节的整数倍,因为头部长度字段的单位是 4 字节。如果选项字段的总长度不足 4 字节的整数倍,需要在选项字段的末尾添加填充字节,直到总长度是 4 的倍数。常用的填充选项是 Kind 1 (NOP) 和 Kind 0 (EOL)。
重要的 TCP 选项详细介绍:
下面介绍一些在现代网络中非常常见和重要的 TCP 选项:
-
End of Option List (EOL) - 选项列表结束
- Kind: 0
- 长度: 1 字节 (没有独立的 Length 字段)
- 作用: 标记选项列表的结束。它只出现在选项列表的末尾,用于指示选项字段的结束位置(通常是因为头部长度字段已经指明了结束位置,EOL 主要用于填充到 4 字节边界)。
-
No Operation (NOP) - 无操作
- Kind: 1
- 长度: 1 字节 (没有独立的 Length 字段)
- 作用: 一个单字节的填充选项。主要用于在选项之间进行填充,以确保后续的选项能够按照一定的字节边界对齐(例如,某些选项可能要求从 2 字节或 4 字节边界开始)。也可以在选项列表末尾用于填充到 4 字节边界。
-
Maximum Segment Size (MSS) - 最大报文段大小
- Kind: 2
- 长度: 4 字节 (Kind 1字节 + Length 1字节 + Option Data 2字节)
- Option Data: 2 字节,表示发送方愿意接收的 TCP 报文段载荷(数据部分)的最大长度(字节)。
- 使用: 在 TCP 连接建立阶段(SYN 报文)由通信双方相互通告。双方会选择其中较小的值作为本次连接实际使用的 MSS。
- 目的: 避免 IP 层进行分片,提高传输效率和可靠性。
-
Window Scale (WS) - 窗口扩大
- Kind: 3
- 长度: 3 字节 (Kind 1字节 + Length 1字节 + Option Data 1字节)
- Option Data: 1 字节,表示"移位计数"(Shift Count, S)。实际的窗口大小是 TCP 头部中 16 位窗口大小字段的值乘以 2S。
- 使用: 在 TCP 连接建立阶段(SYN 报文)协商。
- 目的: 扩展 16 位窗口大小字段的限制,支持高达 216+14=230 字节的窗口大小,在高带宽、高延迟网络中充分利用带宽。
-
Selective Acknowledgment (SACK-Permitted) - 选择性确认允许
- Kind: 4
- 长度: 2 字节 (Kind 1字节 + Length 1字节,没有 Option Data)
- 使用: 在 TCP 连接建立阶段(SYN 报文)由双方通告。如果双方都发送了这个选项,表示本次连接允许使用 SACK 功能。
- 目的: 协商是否启用 SACK 功能。
-
Selective Acknowledgment (SACK) - 选择性确认
- Kind: 5
- 长度: 可变,为 2+8×N,其中 N 是报文段中包含的 SACK 数据块的数量 (N 最大为 4)。
- Option Data: 包含 N 对 4 字节的整数,每对表示一个连续的、接收方已经收到并缓存的不连续数据块的起始和结束序号。
- 使用: 在 TCP 连接建立并协商允许 SACK 后,接收方在发送 ACK 报文时包含此选项,告知发送方收到了哪些乱序的数据块。
- 目的: 当发生丢包时,发送方通过 SACK 信息可以准确知道哪些数据块已经收到但中间有空洞,从而只重传丢失的报文段,提高了重传效率(相比于传统的快速重传只能推测从某个点之后都丢了)。
-
Timestamp (TS) - 时间戳
- Kind: 8
- 长度: 10 字节 (Kind 1字节 + Length 1字节 + Option Data 8字节)
- Option Data: 包含两个 4 字节的字段:
TSval
(Timestamp Value): 发送方当前的时间戳值。TSecr
(Timestamp Echo Reply): 发送方最近一次收到的对方报文中的TSval
值。
- 使用: 在 TCP 连接建立阶段(SYN 报文)协商是否支持;如果支持,后续的报文段中都会包含时间戳选项。
- 目的: 更精确地测量往返时间 (RTT),支持 PAWS (防止序号回绕攻击),并有助于拥塞控制算法。
选项字段的解析:
接收方在接收 TCP 报文时,根据 TCP 头部中的"数据偏移"字段知道整个 TCP 头部(包括选项)的总长度。然后从固定头部(20字节)之后开始,逐个读取和解析选项条目:
- 读取 Kind 字段 (1 字节)。
- 如果 Kind 是 0 (EOL),表示选项结束,停止解析。
- 如果 Kind 是 1 (NOP),这是一个单字节选项,跳过 1 字节,继续解析下一个选项。
- 如果 Kind 是其他值,读取 Length 字段 (1 字节),知道这个选项条目的总长度。
- 根据 Length 跳过剩余的字节(Length - 2 字节是 Option Data 的长度),然后继续解析下一个选项,直到到达头部总长度的末尾。
总结对于tcp报文选项字段解析过程如下:
各个独立的选项条目通过它们自身的 Kind 和 Length 字段来区分。Kind 字段告知接收端选项的类型,而 Length 字段(对于大多数选项)则明确告知接收端这个选项条目有多长,使得接收端可以准确地跳到下一个选项的起始位置进行解析。特殊单字节选项(EOL 和 NOP)则充当结束标记或填充字节,它们的长度是隐含的 1 字节。整个解析过程是基于对这个字段结构的顺序读取。
通过这些可选选项,TCP 协议展现了其可扩展性和适应性,能够根据网络环境和应用需求提供更高级的功能和更好的性能。
关于TCP报文中的一些问题
为什么在建立连接时,初始序列号(ISN)是计算机生成的随机数,而不是从 0 开始?
这是为了防止"旧的重复报文段"干扰新的连接 ,是一个重要的安全性和健壮性设计。
-
潜在问题: 设想一种情况,如果您和服务器之间建立了一个 TCP 连接,发送了一些数据,然后连接正常关闭了。紧接着,您和同一个服务器之间又快速建立了新的 TCP 连接,而且新连接的初始序列号(如果总是从 0 开始)与刚刚关闭的旧连接使用的序列号范围非常接近。
-
旧报文段延迟到达: 如果在旧连接关闭后,网络中仍然有来自旧连接的报文段因为路由延迟或其他原因而滞留,并在新连接建立后才到达接收端。
-
错误识别: 如果新连接的序列号从 0 开始,并且旧连接的序列号范围与之重叠,接收端可能会错误地将这些迟到的旧报文段当作新连接的数据来处理,导致应用程序接收到错误或重复的数据,引起混乱甚至安全问题。
-
随机 ISN 的作用: 通过在每次建立新连接时,选择一个足够随机且基于时间变化的 初始序列号(ISN),可以使得两次不同的连接会话,即使它们发生在同一个端点对(源 IP/端口 -> 目的 IP/端口)之间,使用的序列号范围也极大概率不会重叠 。这样,即使有旧连接的报文段延迟到达,它们的序列号也会远远超出新连接当前有效的接收窗口范围,从而被接收方 TCP 协议栈正确地识别为无效报文段而丢弃,不会干扰到新连接的正常通信。
序列号如果满了的话是从 0 开始继续吗?
是的,正确。
-
TCP 的序列号字段是 32 位的。这意味着序列号的取值范围是 0 到 232−1(大约 40 亿)。
-
当序列号不断增加,达到最大值 232−1 后,如果还需要继续发送数据,它会回绕(Wrap Around),从 0 开始重新计数。
-
回绕带来的问题和解决: 在低速网络下,2^32 的序列号空间足够大,连接的生命周期内很少会发生序列号回绕。但在高带宽网络下,数据传输速度非常快,一个连接可能在很短的时间内就耗尽 40 亿个字节的序号空间,发生回绕。这又可能导致与旧连接的报文段或本连接早期传输的报文段的序号发生重叠,再次引入"旧重复报文段"问题。
-
为了解决这个问题,TCP 引入了时间戳选项(Timestamp Option) 。在连接建立时协商启用时间戳后,每个 TCP 报文段头部都会携带一个发送方的时间戳值。接收方在处理报文段时,不仅会检查序号,还会检查时间戳值。如果收到的报文段的序号虽然在接收窗口内,但其时间戳值比接收方为该连接记录的最近时间戳值还要旧,那么这个报文段就会被认为是回绕后迟到的旧报文段而丢弃 。这个机制被称为 PAWS (Protection Against Wrapped Sequence Numbers)。
所以,序列号会回绕,但通过时间戳等机制来减轻回绕带来的潜在问题。
UDP报文格式介绍
UDP 协议真的非常简,头部只有 8 个字节( 64 位),UDP 的头部格式如下:

目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
校验和:校验和是为了提供可靠的 UDP 首部和数据而设计,和 TCP 校验和一样,也是端到端的,UDP 校验和提供的是对整个 UDP 报文从源主机到目的主机的完整性检查。中间的网络设备(如路由器)通常不检查或修改 UDP 校验和。。
TCP与UDP差别以及各自运用场景
TCP和UDP区别
- 连接
TCP 是面向连接的传输层协议,传输数据前先要建立连接。
UDP 是不需要连接,即刻传输数据。
- 服务对象
TCP 是一对一的两点服务,即一条连接只有两个端点。
UDP 支持一对一、一对多、多对多的交互通信
- 可靠性
TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
UDP 是尽最大努力交付,不保证可靠交付数据。
- 拥塞控制、流量控制
TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
- 首部开销
TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使 用了「选项」字段则会变长的。
TCP报文首部中还有一个记录首部长度的字段,因为TCP 有可变长的「选项」字段,而 UDP 头部长度则是不会变化的,无需多一个字段去记录。
UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
- 传输方式
TCP 是流式传输,没有边界,但保证顺序和可靠。
UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
- 分片不同
TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输 层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完 数据,接着再传给传输层,但是如果中途丢了一个分片,则就需要重传所有的数据包,这样传输 效率非常差,所以通常 UDP 的报文应该小于 MTU。
TCP 和 UDP 应用场景:
由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:
FTP 文件传输
HTTP / HTTPS
由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于:
包总量较少的通信,如 DNS 、 SNMP 等
视频、音频等多媒体通信,这种情景下允许丢包。
TCP三次握⼿过程
TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。

对于 TCP 连接来说,服务器端在没有客户端连接时,确实应该(并且实际上也是如此工作的)时刻保持在"监听(Listening)"状态,等待客户端发起连接请求。
1、第⼀次握⼿:SYN报⽂:
客户端随机初始化序列号 client_isn ,放进TCP⾸部序列号段,然后把SYN置1。把SYN报⽂发送给服务端,表示 发起连接,之后客户端处于 SYN-SENT 状态。
2、第⼆次握⼿:SYN+ACK报⽂:
服务端收到客户端的SYN报⽂之后,会把⾃⼰随机初始化的序号 server_isn 放进TCP⾸部序列号段,「确认应答 号」填⼊ client_isn + 1 ,把SYN和ACK标志位置为1。把SYN+ACK报⽂发送给客户端,然后进⼊ SYN-RCVD 状 态,表示服务器接受了客户端的请求,并希望建⽴连接。
3、第三次握⼿:ACK报⽂:
客户端收到服务端报⽂后,还要向服务端回应最后⼀个应答报⽂。⾸先该应答报⽂ TCP ⾸部 ACK 标志位置为 1 , 其次「确认应答号」字段填⼊ server_isn + 1 ,最后把报⽂发送给服务端,这次报⽂可以携带客户到服务器的 数据,之后客户端处于 ESTABLISHED 状态, 表示客户端已经准备好与服务器进⾏数据传输。 服务器收到客户端的应答报⽂后,也进⼊ ESTABLISHED 状态。 此时,TCP 连接已经建⽴起来,通信双⽅可以开始进⾏数据传输。
TCP三次握手常问问题
第三次握手是否可以携带数据
从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常问的题。
- Socket API 的行为: 标准的网络编程接口(Socket API,如 connect() 和 send())通常会将连接建立和数据发送这两个操作分开。
connect()
函数在第三次握手完成后才返回成功。应用程序通常是在connect()
返回后,才调用send()
函数发送数据的。将数据包含在第三次握手中需要操作系统 TCP/IP 栈提供特殊的 API 支持,或者应用程序对连接过程进行非常规的处理。- 服务器端的处理: 服务器端通常是在调用 accept() 函数接受连接后,才会认为连接正式建立,并准备好接收应用程序数据。在收到第三次握手报文时,可能
accept()
调用还没有返回,服务器应用程序可能还没有完全准备好处理随之而来的应用程序数据。注意:理论上来说TCP 第三次握手可以携带数据 ,因为它是 ACK 报文段。但是,在实际的网络编程和应用中,为了遵循标准的编程模式、保证与各种服务器和中间设备的兼容性,以及简化逻辑,我们通常不会在第三次握手报文段中携带应用程序数据。标准的做法是完成 TCP 三次握手建立连接后,再单独发送第一个包含数据的应用层报文(比如 HTTP 请求)
TCP 的连接状态查看
在 Linux 可以通过 netstat -napt 命令查看,用于查看系统当前的网络状态。
一台电脑可以同时拥有成百上千,甚至几万个或更多的 TCP 连接。

为什么是三次握手,不可以是两/四次吗?
TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。
1. 三次握手可以防止服务器端资源浪费
主要原因是:设计成两次握手的话,由于网络情况问题 ,很可能会导致服务器端进入了类似 ESTABLISHED 的状态,但是实际上本次连接没有成功,客户端不会通过这次TCP连接,服务器端一直白白等待,造成服务器端TCP连接资源浪费。
两次握手造成资源浪费,具体可以分为下面两种情况:
1.假设客户端发送一个 SYN 报文请求建立连接,然后因网络延迟,该报文被滞留在网络中。如果此时服务器没有接到确认就直接建立连接,当滞留的 SYN 报文到达后,服务器可能误以为客户端请求重新连接,错误地建立新的连接,造成了重复历史连接。
这种情况下的图示可以看我的这篇文章:
TCP可靠连接的建立和释放,TCP报文段的格式,UDP简单介绍_tcp syn报文-CSDN博客
2.如果设计成两次握手中,服务器端在收到客户端发来的SYN报⽂后,就会直接进入类似 ESTABLISHED 的状态,同时也会发送给客户端一个SYN+ACK报⽂,但是这个报文也可能在网络中丢失,导致这次TCP连接没有建立起来,但是服务器端一直处于ESTABLISHED状态白白等待。
除了防止服务器资源浪费,三次握手更是为了:
- 确保客户端和服务器都成功地收到了对方的初始序列号。
- 确保客户端成功收到了服务器的确认(SYN-ACK),并将自己的状态迁移告知给服务器(通过第三次 ACK)。
服务器发送 SYN-ACK 后,它并不能确定客户端是否成功收到了这个 SYN-ACK 包。 如果这个 SYN-ACK 在网络中丢失了,客户端将永远不会知道服务器的 ISN (ISN_s),也不知道服务器同意连接了。
「两次握⼿」:⽆法防⽌历史连接的建⽴,会造成双⽅资源的浪费,也⽆法可靠的同步双⽅序列号;
「四次握⼿」:三次握⼿就已经理论上最少可靠连接建⽴,所以不需要使⽤更多的通信次数。
SYN攻击
半连接队列
半连接队列(SYN队列)⽤于存放已经发送了 SYN(同步)包,但还未完成三次握⼿的连接:服务器第⼀次收到客 户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双⽅还没有完全建⽴其连接,服务器会把此种状态下请求连接 放在⼀个队列⾥,我们把这种队列称之为半连接队列。 全连接队列(Accept队列)⽤于存放已经完成三次握⼿,处于完全建⽴连接状态的连接。
在 SYN 攻击中,攻击者发送⼤量伪造的 SYN 请求到⽬标服务器,但不完成后续的握⼿过程,从⽽让服务器⼀直等 待确认,消耗服务器的资源(如半连接队列和系统资源),当半连接队列满了之后,后续再收到SYN报⽂就会丢 弃,导致⽆法与客户端之间建⽴连接。

应对SYN攻击的解决思路
- 缩短 SYN-RECEIVED 状态超时时间:
- 扩大 SYN-RECEIVED 半连接队列(Backlog)大小
- 使用SYN Cookies(这是目前被认为最有效的服务器端应对 SYN Flood 的方法之一)
SYN Cookies 的核心思想和工作方式:
核心思想就是延迟资源分配,资源分配是在接收到第三次握手报文并验证其合法性之后进行
- 服务器在收到 SYN 并发送 SYN-ACK 之后的那个等待阶段(即对应于标准的
SYN-RECEIVED
状态),确实是"无状态"的。 它不会为这个连接请求在内存中创建和维护传统的、消耗资源的连接状态信息(如完整的 TCB 条目)。 - 在这个阶段,服务器也不会为这个 SYN 请求分配显著的、持久性的资源。
- 只有当服务器成功接收到客户端的第三次握手报文(ACK),并且通过 SYN Cookie 验证了这个 ACK 的合法性之后,服务器才会认为这是一个有效的连接,此时才会为这个连接分配必要的资源,创建完整的连接状态,并进入
ESTABLISHED
状态。