Linux Posix API与网络协议栈知识总结

1.TCP三次握手过程?

详细讲解
  1. 第一次握手:

    • 客户端发送一个带有 SYN 标志的数据包,请求与服务器建立连接。
    • 此时客户端会随机选择一个初始序列号 seq = x,并将其写入 TCP 包的序列号字段中。
    • 数据包的关键标志位为 SYN=1
  2. 第二次握手:

    • 服务器收到客户端的 SYN 包后,回复一个带有 SYN + ACK 标志的数据包。
    • 服务器为自己的通信随机选择一个序列号 seq = y
    • 服务器的 TCP 包中包含以下内容:
      • SYN=1:表示服务器同意建立连接。
      • ACK=1:表示确认接收到客户端的 SYN。
      • ack=x+1:确认号表示下一步期望客户端发送的序列号。
  3. 第三次握手:

    • 客户端收到服务器的 SYN+ACK 包后,回复一个 ACK 包。
    • 确认包的内容:
      • ACK=1:确认收到服务器的 SYN。
      • ack=y+1:确认号表示下一步期望服务器发送的序列号。
  4. 连接建立:

    • 当三次握手完成后,客户端和服务器都准备好发送和接收数据。

2.四次挥手?

TCP四次挥手用于可靠断开连接,确保双方通信结束且数据传输完整:

  1. 第一次挥手 :客户端发送一个 FIN 标志的数据包,表示客户端不再发送数据。
  2. 第二次挥手 :服务器收到 FIN 后,回复一个 ACK,表示已收到客户端的请求。
  3. 第三次挥手 :服务器准备关闭连接时,发送 FIN,表示服务器不再发送数据。
  4. 第四次挥手 :客户端收到 FIN 后,回复 ACK ,进入 TIME_WAIT 状态,等待一段时间后真正关闭连接。

关键点:挥手的核心是双方分别关闭发送数据的通道。

3.为什么建立连接需要三次握手,而断开连接需要四次挥手?

1. 建立连接(三次握手)的原因

TCP 建立连接时,需要确保通信双方能够进行可靠的数据传输,并且双方的初始序列号(Sequence Number)能够同步。这是一个双向确认的过程:

  1. 第一次握手

    • 客户端发送一个 SYN(Synchronize)包,表明要建立连接,并携带自己的初始序列号 seq=x
    • 作用 :客户端通知服务器 "我想与你建立连接,我的序列号是 x"。
  2. 第二次握手

    • 服务器收到 SYN 包后,回复一个 SYN+ACK(Acknowledgment)包,表示同意建立连接,并携带自己的初始序列号 seq=y 和对客户端序列号的确认 ack=x+1
    • 作用 :服务器通知客户端 "我收到你的请求,我的序列号是 y,你的序列号 x 我已经收到了"。
  3. 第三次握手

    • 客户端收到服务器的 SYN+ACK 包后,再次发送一个 ACK 包,确认服务器的序列号 y,并表明自己的状态已就绪。
    • 作用 :客户端通知服务器 "我确认了你的序列号 y,我的状态已就绪,可以开始通信了"。
为什么需要三次握手?
  • 防止历史连接干扰: 如果只使用两次握手,有可能服务器收到的是一个延迟的 SYN 包(比如网络延迟导致的历史包),服务器会误以为客户端要建立新连接,并回复 SYN+ACK。但客户端如果不再需要此连接,就不会发送 ACK,导致服务器资源浪费或状态异常。
  • 确保双方能力同步 : 三次握手能确保双方发送和接收能力都正常:
    • 第一次:客户端 -> 服务器,测试客户端发送能力、服务器接收能力。
    • 第二次:服务器 -> 客户端,测试服务器发送能力、客户端接收能力。
    • 第三次:客户端 -> 服务器,确认双方状态和能力都已就绪。

2. 断开连接(四次挥手)的原因

TCP 连接是全双工的,即通信双方可以独立地发送和接收数据。断开连接时,需要分别关闭这两个方向的通道,因此需要四次挥手:

  1. 第一次挥手

    • 客户端发送 FIN 包,表示自己不再发送数据了(但仍能接收数据)。
    • 状态变化
      • 客户端:ESTABLISHED -> FIN_WAIT_1
    • 作用:客户端通知服务器 "我不再发送数据了,请关闭发送通道"。
  2. 第二次挥手

    • 服务器收到 FIN 包后,发送 ACK 包确认。
    • 状态变化
      • 服务器:ESTABLISHED -> CLOSE_WAIT
      • 客户端:FIN_WAIT_1 -> FIN_WAIT_2
    • 作用:服务器通知客户端 "我已收到你的请求,但我还有数据要发,请稍等"。
  3. 第三次挥手

    • 服务器发送自己的 FIN 包,表示自己也不再发送数据了。
    • 状态变化
      • 服务器:CLOSE_WAIT -> LAST_ACK
      • 客户端:FIN_WAIT_2 -> TIME_WAIT
    • 作用:服务器通知客户端 "我不再发送数据了,你可以关闭连接了"。
  4. 第四次挥手

    • 客户端收到 FIN 包后,发送 ACK 包确认,进入 TIME_WAIT 状态,并等待一段时间(2MSL)以确保服务器能收到 ACK。
    • 状态变化
      • 客户端:TIME_WAIT -> CLOSED
      • 服务器:LAST_ACK -> CLOSED
    • 作用:客户端通知服务器 "我已确认你的请求,可以安全关闭了"。

3. 为什么建立连接需要三次握手,而断开连接需要四次挥手?
建立连接(三次握手)
  • 连接的建立是双方协商的过程,客户端和服务器都需要确认对方的发送和接收能力。
  • 三次握手已经足够完成序列号的同步和能力确认:
    1. 第一次:客户端发送 SYN,通知服务器自己有能力发起连接。
    2. 第二次:服务器回复 SYN+ACK,通知客户端自己有能力接收连接请求。
    3. 第三次:客户端发送 ACK,通知服务器自己收到了确认,连接建立完成。
断开连接(四次挥手)
  • TCP 是 全双工协议,需要双方独立地关闭发送和接收通道:

    1. 客户端发送 FIN 表明 "我不再发送数据"。
    2. 服务器确认客户端的 FIN,同时表明 "我还有数据需要发送"。
    3. 服务器发送自己的 FIN 表明 "我也不再发送数据了"。
    4. 客户端确认服务器的 FIN,确保双方都完成断开。
  • 由于接收和发送的关闭是独立的,双方需要额外一次握手来确认对方的 FIN 请求,因而需要 四次挥手

4.TIME_WAIT状态持续时间及原因

1. 什么是 TIME_WAIT 状态?
  • TIME_WAIT 是 TCP 连接在断开后的一个重要状态,出现在客户端在 四次挥手的最后一步 确认了服务器的 FIN 包后。
  • 在这个状态下,客户端不会立刻关闭连接,而是保持 2MSL(Maximum Segment Lifetime,报文最大生存时间) 的时间,以确保旧的连接数据不会干扰后续新连接。
2. TIME_WAIT 状态的持续时间
  • TIME_WAIT 的持续时间是 2倍的 MSL ,即 2MSL
  • MSL(Maximum Segment Lifetime) 是一个报文段在网络中的最长存活时间,由协议栈设定,通常为 30 秒到 2 分钟 。具体取决于操作系统配置:
    • Linux 默认 MSL 为 30 秒 ,TIME_WAIT 时间为 2 × 30 = 60 秒
    • Windows 默认 MSL 为 2 分钟 ,TIME_WAIT 时间为 2 × 2 = 4 分钟
3. 为什么需要 TIME_WAIT 状态?

TIME_WAIT 状态的存在主要基于 TCP 的可靠性设计,目的是避免旧连接的数据干扰新连接,并确保通信的完整性。其作用包括以下两点:


(1) 确保最终 ACK 包能够到达对方
  • 四次挥手的最后一步
    • 客户端发送 ACK 包以确认服务器的 FIN 包。
    • 如果这个 ACK 包丢失,服务器会重发 FIN,客户端需要重发 ACK 来确认。
  • TIME_WAIT 的意义
    • 客户端在 TIME_WAIT 状态下,保留连接状态和资源,以应对服务器可能的 FIN 重传。
    • 确保对方知道连接已经安全关闭,防止服务器认为连接未正常断开。

(2) 防止旧连接的数据干扰新连接
  • 在 TCP 中,某些旧的报文可能因网络延迟而滞留在网络中(比如延迟的 FIN、ACK 或数据包)。
  • 如果立即关闭连接,新建立的连接可能会重复使用相同的四元组(源 IP、源端口、目标 IP、目标端口)。
  • 旧连接中的数据可能与新连接冲突,导致数据混淆或错误。
  • TIME_WAIT 的意义
    • 等待足够时间(2MSL),保证旧连接的所有数据包在网络中消失。
    • 避免旧报文干扰新连接的正常通信。

4. TIME_WAIT 状态的核心特点
特性 说明
状态触发条件 客户端主动关闭连接后,在发送 ACK 包后进入 TIME_WAIT 状态。
持续时间 2MSL,通常为 60 秒到 4 分钟,具体取决于操作系统的配置。
资源占用 客户端在 TIME_WAIT 状态期间保留连接的资源(如端口号等)。
状态退出条件 TIME_WAIT 时间耗尽,连接彻底关闭,资源释放。
作用 确保最终 ACK 包被对方收到,同时避免旧连接的数据干扰新连接。

5. TIME_WAIT 状态的常见问题
  1. 大量 TIME_WAIT 导致端口耗尽

    • 在高并发场景下,如果客户端频繁主动断开连接,会产生大量 TIME_WAIT 状态,占用系统资源(如端口号)。
    • 解决方法
      • 使用长连接,减少连接的建立和断开次数。
      • 调整系统参数(如缩短 TIME_WAIT 时间或启用端口复用):
        • Linux 中可以启用 tcp_tw_reusetcp_tw_recycle 参数(注意:tcp_tw_recycle 已在新内核中被废弃)。
        • 例如:echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
  2. 服务器端为什么很少进入 TIME_WAIT?

    • 按照 TCP 协议规范,主动断开连接的一方 才进入 TIME_WAIT 状态。
    • 通常客户端是主动发起断开的一方,因此客户端更容易进入 TIME_WAIT 状态。
    • 如果服务器端主动断开,也会进入 TIME_WAIT。

6. TIME_WAIT 状态的示例

假设客户端(A)与服务器(B)建立连接并通信,随后客户端主动关闭连接:

  1. 客户端 A 向服务器 B 发送 FIN 包,进入 FIN_WAIT_1 状态。
  2. 服务器 B 收到 FIN 后,回复 ACK,进入 CLOSE_WAIT 状态。
  3. 客户端 A 收到 ACK 后,进入 FIN_WAIT_2 状态。
  4. 服务器 B 发送 FIN 包,进入 LAST_ACK 状态。
  5. 客户端 A 收到 FIN 包后,发送 ACK,进入 TIME_WAIT 状态,并等待 2MSL 时间。
  6. 如果服务器没有再发送 FIN 包,客户端 A 超过 2MSL 时间后,进入 CLOSED 状态。

7. 总结
  • TIME_WAIT 持续时间:2MSL,具体时间由操作系统设置(通常 60 秒或 240 秒)。
  • TIME_WAIT 的原因
    1. 确保服务器收到 ACK 包,防止连接断开不完全。
    2. 避免旧连接数据干扰新连接的通信。
  • 应对方法
    • 减少短连接的使用,采用长连接策略。
    • 调整操作系统参数以优化资源使用(如启用端口复用)。

5.超时重传和快速重传

TCP 使用重传机制来保证数据传输的可靠性,超时重传和快速重传是 TCP 实现可靠传输的重要手段。


1. 超时重传 (Timeout Retransmission)
1.1 定义
  • 当 TCP 发送一个数据包后,未在指定时间内收到对应的 ACK(确认包),发送方会认为数据丢失或对方未能成功接收,于是触发 超时重传
  • 超时时间的长短由 TCP 的 重传定时器(RTO, Retransmission Timeout) 决定。

1.2 超时重传的触发条件
  1. 发送方发送数据包后,未收到对应的 ACK。
  2. 超过 RTO 的时间,触发超时重传。

1.3 超时重传的特点
  • 重传间隔动态变化

    • TCP 会根据网络状况调整 RTO。
    • 通过 RTT (Round-Trip Time, 往返时间)RTTVAR (RTT Variance, RTT 方差) 动态计算。
    • 常见算法:
      • RTO = SRTT + 4 × RTTVAR
      • SRTT(平滑往返时间):加权平均 RTT。
      • RTTVAR(方差):衡量 RTT 波动。
  • 指数退避(Exponential Backoff)

    • 若超时重传失败,RTO 会指数增加(如 1 秒 → 2 秒 → 4 秒 → 8 秒等)。
    • 避免过度重传导致网络拥塞。

1.4 超时重传的流程
  1. 发送方发送数据包,并启动计时器。
  2. 若在 RTO 时间内收到 ACK,停止计时器。
  3. 若超时未收到 ACK,重新发送数据包,同时将 RTO 值加倍。
  4. 重复步骤 2 和 3,直到收到 ACK 或超过最大重传次数(通常为 5~12 次,操作系统可配置)。

1.5 超时重传的优缺点
优点 缺点
简单易实现,保证可靠性。 反应速度慢,必须等待超时时间到达后才触发。
适用于复杂网络环境和高延迟场景。 当网络丢包率高时,可能导致较长延迟。

2. 快速重传 (Fast Retransmission)
2.1 定义
  • 快速重传是一种优化的重传机制,发送方无需等待 RTO 超时即可快速识别数据包丢失并进行重传。
  • 利用 重复 ACK(Duplicate ACK, DUPACK) 的特性判断数据包丢失:
    • 当接收方检测到数据包丢失,会对已接收到的数据重复发送相同的 ACK。
    • 发送方收到 3 个以上重复 ACK,即认为某个数据包丢失并触发快速重传。

2.2 快速重传的触发条件
  1. 接收方按序接收数据,发现中间的某个数据包丢失。
  2. 接收方对最后一个正确接收的数据包重复发送 ACK。
  3. 发送方收到 3 个或以上重复的 ACK 时,触发快速重传。

2.3 快速重传的流程
  1. 发送方发送数据包,接收方接收部分数据。
  2. 若接收方发现某个数据包未到达,会重复发送 ACK,告知发送方缺失的包号。
  3. 发送方在收到 3 个 DUPACK 时,立即重传丢失的数据包,而不等待 RTO 超时。
  4. 接收方收到重传的包后,正常确认并继续通信。

2.4 快速重传的特点
  • 更快的响应速度
    • 无需等待超时触发,可以迅速定位并修复丢包问题。
  • 配合快速恢复(Fast Recovery)
    • 快速重传通常与快速恢复配合使用,在丢包时避免大幅降低发送速率。

2.5 快速重传的优缺点
优点 缺点
比超时重传更快,减少网络延迟。 需要较好的网络状况,否则可能引发错误重传。
提升高吞吐量网络中的传输效率。 对小窗口传输效果有限(因无法满足 3 个 DUPACK)。

3. 超时重传与快速重传的对比
特性 超时重传 快速重传
触发条件 未在 RTO 内收到 ACK 收到 3 个 DUPACK
触发时间 RTO 超时后触发 丢包后立即触发,快于超时重传
适用场景 复杂网络环境,网络波动较大或高延迟的场景 网络稳定、丢包率低、高吞吐场景
响应速度
复杂性 简单 较复杂,需要接收方支持重复 ACK 机制
可靠性 高,适应性强 在高丢包率场景下,误判率较高

4. 示例解释
场景一:超时重传

假设发送方 A 发送数据包 #1,并等待 ACK,但该数据包丢失:

  1. A 启动 RTO 定时器,等待接收方 B 的 ACK。
  2. RTO 时间到达,A 发现未收到 ACK,触发超时重传。
  3. A 重新发送数据包 #1,并重新启动定时器。

场景二:快速重传

假设发送方 A 发送数据包 #1#2#3,但 #2 丢失:

  1. 接收方 B 收到 #1#3,发现缺失数据包 #2,于是重复发送 ACK #1
  2. A 收到 3 个 DUPACK(ACK #1),立即重传数据包 #2
  3. B 收到数据包 #2 后,继续接收后续数据。

5. 总结
  • 超时重传 通过 RTO 机制确保可靠性,适用于高延迟网络,但响应较慢。
  • 快速重传 通过重复 ACK 快速识别丢包,提高传输效率,但需要稳定的网络支持。
  • 在现代 TCP 实现中,快速重传配合快速恢复(Fast Recovery)共同使用,以最大程度优化传输性能。

6.TCP首部长度与字段解析

1. TCP 首部长度

TCP 首部的长度至少为 20 字节 (固定部分),如果包含可选字段(Options),则首部长度会增加,但最大不能超过 60 字节

  • 首部长度字段单位 :以 4 字节(32 位)为单位。
    • 最小值:20字节 / 4 = 5(即首部长度字段值为 5)。
    • 最大值:60字节 / 4 = 15(即首部长度字段值为 15)。

2. TCP 首部字段划分

TCP 首部由固定部分和可选部分组成:

字段名称 大小 说明
源端口号(Source Port) 16 位 标识数据来源的端口号。
目的端口号(Destination Port) 16 位 标识数据要发送到的目标端口号。
序列号(Sequence Number) 32 位 用于数据流的排序,标识发送方发送的第一个字节的序号。
确认号(Acknowledgment Number) 32 位 标识接收方期望的下一个字节序号,用于确认收到的数据。
数据偏移(Header Length) 4 位 指示 TCP 首部的长度,单位为 4 字节。
保留位(Reserved) 3 位 保留为未来使用,通常为 0。
控制标志(Flags) 9 位 包括 6 个常见控制位(SYN、ACK、FIN 等),用于控制连接的状态。
窗口大小(Window Size) 16 位 用于流量控制,表示接收方可接收的窗口大小(单位为字节)。
校验和(Checksum) 16 位 验证 TCP 报文的完整性,包含首部和数据部分。
紧急指针(Urgent Pointer) 16 位 指示紧急数据的位置,仅当 URG 标志置位时有效。
选项(Options) 可变 包括时间戳、窗口扩展等扩展信息。
填充(Padding) 可变 补齐到 4 字节的倍数,确保首部对齐。

3. TCP 首部字段详解
3.1 固定部分(20 字节)
  1. 源端口号 (Source Port, 16 位)

    • 标识发送方的端口号。
    • 用于区分应用层的不同进程。
  2. 目的端口号 (Destination Port, 16 位)

    • 标识接收方的端口号。
    • 用于目标主机确定接收数据的应用程序。
  3. 序列号 (Sequence Number, 32 位)

    • 用于排序数据。
    • 表示当前报文段中第一个字节的序号。
    • 在三次握手的 SYN 报文 中,序列号起始值为随机数。
  4. 确认号 (Acknowledgment Number, 32 位)

    • 用于确认接收数据。
    • 表示接收方期望接收到的下一个字节的序号。
    • 只有当 ACK 标志置位时该字段才有效。
  5. 数据偏移 (Data Offset, 4 位)

    • 表示 TCP 首部的长度。
    • 单位为 4 字节,取值范围 515
  6. 保留位 (Reserved, 3 位)

    • 为未来协议扩展预留,目前必须设置为 0
  7. 控制标志 (Flags, 9 位)

    • 用于控制 TCP 连接和数据传输状态:
      • URG:表示紧急数据。
      • ACK:确认字段有效。
      • PSH:要求接收方立即交付数据。
      • RST:重置连接。
      • SYN:同步序列号,用于建立连接。
      • FIN:结束数据发送,用于释放连接。
  8. 窗口大小 (Window Size, 16 位)

    • 表示接收方的接收缓冲区剩余空间,单位是字节。
    • 通过滑动窗口机制实现流量控制。
  9. 校验和 (Checksum, 16 位)

    • 用于验证报文的完整性,覆盖 TCP 首部和数据部分。
  10. 紧急指针 (Urgent Pointer, 16 位)

    • 指向紧急数据的最后一个字节的位置,配合 URG 标志使用。

3.2 可选部分(选项 + 填充)
  1. 选项字段(Options)
    • 用于扩展 TCP 功能,常见选项:
      • 最大报文段长度(MSS, Maximum Segment Size)
        • 用于协商双方允许的最大数据段大小。
      • 时间戳(Timestamp)
        • 用于 RTT 测量与 PAWS 保护。
      • 窗口扩大因子(Window Scale)
        • 用于扩大窗口大小。
      • SACK(Selective Acknowledgment, 选择性确认)
        • 支持接收方告知发送方哪些数据已正确接收。
  2. 填充字段(Padding)
    • 为了保证选项字段的字节数对齐到 4 的倍数。

7. TCP 在 listen 时的参数 backlog 的意义

概念解释
  • 在 TCP 服务端编程中,listen 函数会将一个套接字设置为被动监听状态,以等待客户端发起连接请求。

  • 其函数原型通常如下(以 Linux 为例):

    cpp 复制代码
    int listen(int sockfd, int backlog);
    • sockfd:服务端套接字文件描述符。
    • backlog半连接队列和全连接队列的总和(或近似),定义了在套接字上可以排队的最大连接请求数量。

TCP 的连接排队机制

TCP 在 listen 状态时,涉及两个关键队列:

  1. 半连接队列(Incomplete Connection Queue)

    • 存储接收到客户端的 SYN 报文,但三次握手尚未完成的连接请求。
    • 这些连接处于 SYN_RCVD 状态。
  2. 全连接队列(Complete Connection Queue)

    • 存储已完成三次握手但尚未由应用程序调用 accept() 的连接。
    • 这些连接处于 ESTABLISHED 状态。

backlog 的作用

backlog 定义了上面两个队列中可容纳的最大连接数,用来控制并发连接的接收能力。

  • 实际含义

    它的作用是设置 全连接队列 的最大长度,通常还间接影响半连接队列的大小(内核实现不同)。如果队列中的连接数超过 backlog 限制,新连接将被拒绝或忽略。

    具体表现:

    • 如果半连接队列满,客户端的 SYN 报文 被丢弃。
    • 如果全连接队列满,新完成三次握手的连接将被拒绝,客户端会收到 RST

backlog 的值如何确定
  • 实际值可能与传入值不同

    操作系统会根据配置或实际能力调整 backlog 的值:

    • 在 Linux 中,backlog 值被截断为 max(1, min(backlog, somaxconn)),其中 somaxconn 是内核参数,表示最大允许值,默认 128。

    • 可通过以下命令查看或设置:

      cpp 复制代码
      sysctl net.core.somaxconn
      sysctl -w net.core.somaxconn=1024
  • 如何设置合理的值

    取决于服务器的负载和应用场景。

    • 如果连接量很大,建议设置较高的 backlog 值,并调整内核参数 somaxconn
    • 如果是低并发应用,默认值通常已足够。

连接队列相关状态变化

以下是 backlog 参数在连接处理中的影响示意流程:

复制代码

常见问题与解答
  1. backlog 太小会导致什么问题?

    • 半连接队列或全连接队列可能快速填满。
    • 导致新连接被拒绝,客户端可能收到 RST 或体验连接超时。
  2. backlog 的值需要动态调整吗?

    • 根据负载需求调整是常见优化手段。
    • 高并发场景中,通过增大 backlog 和调整 somaxconn 可提高性能。
  3. 如果 backlog 设置得过大,会有什么问题?

    • 过大的值可能浪费内存资源,尤其是半连接队列需要为每个连接分配一定资源。
    • 应根据系统容量和预期连接量选择适当的值。
  4. accept() 在什么时候调用?

    • accept()全连接队列 中取出已完成三次握手的连接,并为应用层分配一个新的文件描述符。

总结
  • backlog 的意义
    控制服务器在高并发情况下能够处理的最大连接数,间接影响队列长度和连接接受能力。
  • 推荐设置
    在高并发环境中,结合 somaxconn 和应用需求适当增大 backlog 值,但避免无意义的超大设置。

8. Accept 发生在三次握手的哪一步?

accept() 函数是服务端在 TCP 三次握手完成 之后 执行的操作,用于从 全连接队列 中取出已完成三次握手的连接。


具体时序说明

在三次握手的过程中:

  1. 第一步:客户端发送 SYN

    • 客户端向服务端发送 SYN 报文,表示请求建立连接。
    • 服务端收到后,进入 SYN_RCVD 状态,将连接放入 半连接队列
  2. 第二步:服务端发送 SYN+ACK

    • 服务端响应客户端的 SYN ,同时发送自己的 SYN
    • 此时连接仍在 半连接队列 中。
  3. 第三步:客户端发送 ACK

    • 客户端收到服务端的 SYN+ACK 后,发送 ACK 确认。
    • 服务端收到 ACK 后,认为三次握手完成,连接进入 ESTABLISHED 状态,并从 半连接队列 转移到 全连接队列

accept() 的执行时机
  • 服务端调用 accept()
    • accept()全连接队列 中取出一个完成三次握手的连接。
    • 然后分配一个新的套接字(与监听套接字不同),用于处理该连接。
时序图表示

关键点总结
  1. accept() 总是在三次握手完成之后调用 ,否则连接不会进入 全连接队列
  2. accept() 的主要作用:
    • 检索 全连接队列 中已完成三次握手的连接。
    • 为该连接分配一个新的文件描述符,以便服务端应用程序与客户端通信。
  3. 三次握手完成与 accept() 的逻辑独立:
    • 三次握手是 TCP 协议栈处理的。
    • accept() 是应用层的操作,用于处理已建立的连接。

9. 三次握手过程中的不安全性

TCP 的三次握手设计旨在可靠建立连接,但由于其依赖网络传输,无法完全防范某些安全威胁。以下是三次握手中常见的不安全性及其原因:


1. SYN Flood 攻击
  • 原理
    • 攻击者向服务端发送大量伪造的 SYN 包,但不发送后续的 ACK
    • 服务端收到 SYN 后,将连接放入 半连接队列 ,并进入 SYN_RCVD 状态。
    • 半连接队列被耗尽后,服务端无法处理新的合法连接请求。
  • 影响
    • 服务端资源耗尽,合法用户无法建立连接。
  • 解决方案
    • SYN Cookies
      • 服务端不直接为每个 SYN 分配资源,而是使用加密算法生成一个 cookie,并将其嵌入 SYN+ACK 中。
      • 只有客户端发送有效的 ACK 时,才为其分配资源。
    • 调整队列大小
      • 增大半连接队列长度(调整内核参数,如 net.ipv4.tcp_max_syn_backlog)。
    • 防火墙过滤
      • 配置防火墙或使用工具(如 iptables)过滤可疑的 SYN 包。

2. IP Spoofing
  • 原理
    • 攻击者伪造客户端的 IP 地址,向服务端发送 SYN 包。
    • 服务端响应 SYN+ACK,但伪造的地址无实际主机或无法发送 ACK,导致连接无法完成。
  • 影响
    • 可能配合 SYN Flood 攻击。
    • 伪造地址的攻击者无法真正建立连接,但可能用来掩盖其他攻击源。
  • 解决方案
    • 验证客户端 IP 地址
      • 配置访问控制列表(ACL)以限制特定 IP 段的连接请求。
    • 网络追踪
      • 监控可疑的 IP 流量行为,定位攻击源。

3. 中间人攻击(Man-in-the-Middle Attack, MITM)
  • 原理
    • 攻击者截获客户端和服务端之间的通信,并伪装成双方。
    • 通过篡改数据包,攻击者可以监控或操控通信内容。
  • 影响
    • 数据可能被窃取、篡改或伪造,造成通信安全性失效。
  • 解决方案
    • 使用 TLS 加密
      • 在 TCP 三次握手完成后,建立基于 TLS 的加密通信通道,防止通信被监听或篡改。
    • 验证主机身份
      • 使用证书验证机制,确保通信双方身份真实。

4. 数据包伪造攻击
  • 原理
    • 攻击者伪造或篡改三次握手中的数据包(SYN、SYN+ACK、ACK)。
    • 伪造的 ACK 包可能让服务端误以为连接已建立,进而浪费资源。
  • 影响
    • 攻击者可能扰乱正常的连接建立过程。
    • 服务端可能错误分配资源给无效连接。
  • 解决方案
    • 序列号验证
      • TCP 的三次握手依赖随机的序列号,增加伪造难度。
    • 网络安全监测
      • 使用入侵检测系统(IDS)监控异常流量。

5. Replay 攻击
  • 原理
    • 攻击者拦截一个合法的三次握手序列,并在稍后重新发送相同的 SYN 包。
    • 服务端可能将旧连接视为新连接,导致资源被滥用。
  • 影响
    • 资源浪费。
    • 配合其他攻击可能干扰正常通信。
  • 解决方案
    • 时间戳选项(TCP Timestamp)
      • 使用 TCP 的时间戳选项,检测并拒绝旧数据包。
    • 加密握手内容
      • 结合加密手段确保数据包的唯一性。

6. 隧道攻击(Tunneling Attack)
  • 原理
    • 攻击者利用合法的 TCP 三次握手,建立与恶意服务器的通信隧道。
    • 在连接建立后,恶意服务器可能发送大量垃圾数据或植入恶意代码。
  • 影响
    • 增加服务端负担。
    • 恶意内容可能对服务器或用户造成威胁。
  • 解决方案
    • 流量监控
      • 实时监控已建立连接的流量,检测异常行为。
    • 数据审计
      • 在应用层审查数据内容,识别恶意数据。

总结

TCP 三次握手本身是无状态的连接过程,其不安全性通常源于攻击者利用协议漏洞或未充分验证的流量。为防范这些威胁,可以采取以下措施:

  1. 网络层防护:启用 SYN Cookies、防火墙规则。
  2. 传输层加固:结合 TLS 加密保护通信内容。
  3. 流量监控:利用入侵检测系统(IDS)和日志分析及时发现异常行为。

10. TCP 与 UDP 的区别

TCP 和 UDP 是两种常见的传输层协议,主要区别体现在 连接模式可靠性数据传输方式使用场景 等方面。


1. 基本特性对比
特性 TCP UDP
连接模式 面向连接:通信前需要三次握手建立连接。 无连接:直接发送数据,无需建立连接。
传输可靠性 提供可靠传输,确保数据无丢失、无重复、按序到达。 不保证可靠传输,数据可能丢失、重复或乱序。
传输方式 面向字节流:数据以连续的字节流形式传输。 面向报文:数据以独立的报文(Datagram)传输。
流量控制 有流量控制与拥塞控制机制(如窗口大小调整)。 无流量控制,发送方可以以任意速度发送数据。
首部开销 较大:20~60 字节,包含序列号、确认号等。 较小:8 字节,只有必要的控制字段。
通信效率 较低:因为有连接管理、数据校验等机制。 较高:无连接管理和校验,开销更小。
使用场景 适用于可靠性要求高的场景,如文件传输、网页浏览。 适用于实时性要求高的场景,如视频直播、DNS 查询。

2. TCP 的特点
1) 面向连接
  • 在通信开始前,TCP 通过三次握手建立连接,确保双方能可靠地传输数据。
  • 通信结束时,通过四次挥手断开连接。
2) 提供可靠性
  • 序列号:每个数据包都有序列号,用于标识数据的顺序。
  • 确认机制:接收方通过 ACK 报文确认已收到的数据。
  • 超时重传:若发送方在指定时间内未收到 ACK,会重传数据。
  • 数据校验:通过校验和验证数据完整性。
3) 面向字节流
  • TCP 将应用层的数据分割成多个段(Segment),以流的形式传输,数据之间没有明确边界。
4) 流量控制和拥塞控制
  • 流量控制:通过接收方的窗口大小,调节发送方的发送速度,避免接收方过载。
  • 拥塞控制:避免网络拥塞,常用算法包括慢启动、拥塞避免等。

3. UDP 的特点
1) 无连接
  • 发送数据前无需建立连接,直接将数据报(Datagram)发送到目标地址。
  • 不会追踪或管理状态,每次通信是独立的。
2) 不可靠
  • UDP 不保证数据的完整性和顺序,可能出现丢包、重复或乱序。
  • 不会提供确认机制,也不支持重传。
3) 面向报文
  • UDP 以独立的报文为单位传输数据,每个报文都保留了完整性。
  • 应用程序需要自行拆分或重组数据。
4) 高效
  • UDP 的头部只有 8 字节,数据传输效率较高。
  • 适合对实时性要求较高、丢包影响较小的场景。

4. 应用场景对比
场景 TCP UDP
文件传输 HTTP(S)、FTP、SMTP 等。 不适合(无法保证数据可靠性)。
实时通信 视频通话(可靠性优先时)。 视频直播、语音通话、在线游戏(实时性优先)。
网络配置 不常用。 DHCP、DNS、SNMP 等轻量级协议。
流媒体传输 需要稳定的传输(如高清视频)。 需要快速传输(如低延迟直播)。
大规模广播或组播 不支持。 支持(如 IPTV、局域网广播)。

5. TCP 与 UDP 的首部对比
字段 TCP 首部 UDP 首部
源端口号 16 位,标识发送方端口号。 16 位,标识发送方端口号。
目标端口号 16 位,标识接收方端口号。 16 位,标识接收方端口号。
序列号 32 位,标识数据段序号。 无。
确认号 32 位,确认接收的最后一个序列号。 无。
数据偏移 表示首部长度。 无。
标志位 包括 SYN、ACK、FIN、PSH、URG 等控制标志。 无。
窗口大小 用于流量控制。 无。
校验和 验证数据完整性。 验证数据完整性。
紧急指针 标识紧急数据的位置。 无。

6. 总结对比
  • TCP 是可靠的、面向连接的协议,适合对数据完整性和顺序有要求的场景。
  • UDP 是简单、高效的无连接协议,适合对实时性要求高的场景。
相关推荐
p-knowledge9 分钟前
容器设计模式:Sidecar
网络协议·设计模式·rpc
重生之我是数学王子1 小时前
ARM体系架构
linux·c语言·开发语言·arm开发·系统架构
kingbal1 小时前
RabbitMQ:windows系统安装
linux·分布式·rabbitmq
earthzhang20211 小时前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
vvw&1 小时前
如何在 Ubuntu 上安装 NodeBB 并使用 Nginx 反向代理
linux·运维·服务器·nginx·ubuntu·github·论坛
网硕互联的小客服1 小时前
如何排查服务器是否有被黑客入侵的迹象?
linux·运维·服务器·windows
程序设计实验室2 小时前
硬盘空间消失之谜:Linux 服务器存储排查与优化全过程
linux
靡樊2 小时前
Linux:进程(环境变量、程序地址空间)
linux
编码追梦人2 小时前
举例说明如何在linux下检测摄像头设备具备的功能
linux