计算机网络八股4


✅TCP 为什么可靠?(TCP 的可靠性机制)


TCP(Transmission Control Protocol)被设计为一个面向连接的、可靠的、字节流服务协议,其可靠性体现在以下几个关键机制上:


🧱 一、连接管理机制(连接前提保障)

✅ 1. 三次握手(Three-way Handshake)

  • 作用:确保通信双方都能正常发送/接收,并同步初始化序列号。
  • 特点:防止历史报文造成混乱,确认双向可达。

✅ 2. 四次挥手(Four-way Handshake)

  • 作用:确保连接可以双向关闭,所有数据传输完毕。

🧪 二、数据可靠性机制(五大核心保障)

机制 功能解释
① 校验和 Checksum 检测数据在传输过程中是否发生损坏。包括 TCP 报文头部、数据及伪首部。发现错误则丢弃该报文段。
② 序列号与确认应答 每个数据段都标注序列号,接收方发回 ACK 确认号,确保数据有序到达、完整重组
③ 超时重传机制 如果发送方超时未收到 ACK,会自动重新发送数据,确保数据不丢失。
④ 滑动窗口(流量控制) 控制发送速率,根据接收方的处理能力(窗口大小)调整,防止对方来不及接收
⑤ 拥塞控制 避免网络拥塞和过载,动态调整发送速率,确保整体网络不崩溃

🧮 三、每项机制的详细解释


🔍 ① 校验和(Checksum)

  • 每个 TCP 报文段都包含一个 16 位校验和
  • 内容覆盖:TCP 报文头 + 数据 + 伪首部(IP地址等信息)。
  • 接收方重新计算校验和,如发现不一致,则丢弃该报文段,保证数据不被篡改或损坏。

🔄 ② 序列号 / 确认应答(Sequence Number / ACK)

  • TCP 把数据分段,每个段打上序列号。
  • 接收方收到后会发送确认号(ACK),表示“我已经收到你第 N 个字节,期待下一个字节是 N+1”。
  • 若超时未收到确认,发送方就会重传该段。

⏱️ ③ 超时重传(Timeout Retransmission)

  • 每次发送数据都会开启一个定时器。
  • 如果在一定时间内没收到 ACK,则重新发送数据。
  • 超时值会根据 RTT(往返时间)动态调整。

🪟 ④ 滑动窗口(Sliding Window)

  • 保证发送方不会一次发送太多数据导致接收方来不及处理。
  • 接收方告诉发送方:“我还有 N 字节的空间可以接收”。
  • 实现了流量控制,防止接收方缓存溢出。

🌐 ⑤ 拥塞控制(Congestion Control)

包含四个关键算法:

名称 说明
慢启动 初始发送窗口较小,逐步指数增长
拥塞避免 达到阈值后线性增长,避免网络过载
快重传 收到三个重复 ACK 后,立刻重传丢失数据
快恢复 快速从拥塞状态恢复,避免重启慢启动

🧠 四、TCP 可靠性的总结图解(逻辑结构)

1
2
3
4
5
6
7
8
9
                TCP 可靠性机制
┌──────────────┐
│ 三次握手/四次挥手 │
└──────────────┘

┌──────────┬──────────┬────────────┬────────────┬─────────────┐
│ 校验和 │ 序列号+ACK │ 超时重传 │ 滑动窗口 │ 拥塞控制 │
│ 检错丢弃 │ 确保顺序和完整 │ 确保必达 │ 控制流量 │ 控制网络负载 │
└──────────┴──────────┴────────────┴────────────┴─────────────┘

🧾 五、总结一句话:

TCP 的可靠性不是靠某一个机制,而是通过三次握手、确认应答、重传机制、流量控制、拥塞控制等多维度保障,在复杂网络环境下确保数据“不丢、不乱、不重、不慢”。


✅TCP 的流量控制(Flow Control)


🧭 一、流量控制是什么?

流量控制是为了防止发送方发送数据过快,导致接收方来不及接收,从而发生数据丢失或缓冲区溢出。

📌 关键机制:滑动窗口(Sliding Window) 📌 控制方向:由接收方控制发送方


🧱 二、滑动窗口机制详解

滑动窗口是一种窗口大小可变的机制,反映了接收方还有多少空间可以接收数据。 该值会在 TCP 报文的首部中的窗口大小字段(Window)中携带。


🧮 三、示例流程分析(假设窗口初始为 400 字节)

以下通过具体数字演示:

✅ 1. 初始状态

  • 接收窗口大小 REV.WND = 400 字节
  • 发送窗口也初始化为 400 字节

✅ 2. 第一次发送

  • 发送方发送了 200 字节
  • 自己的发送窗口剩余 200
  • 接收方更新 REV.WND = 400 - 200 = 200,通过 ACK 报文返回窗口大小 200

✅ 3. 第二次发送

  • 又发送 200 字节
  • 接收方因负载过高,只处理了 100 字节,另外 100 被缓存在缓冲区
  • 此时 REV.WND = 400 - 200 - 100 = 100,回包中返回窗口为 100

✅ 4. 第三次发送

  • 再发送 100 字节
  • 接收窗口已满:REV.WND = 0,发送方收到后暂停发送

✅ 5. 轮询机制

  • 发送方会开启定时器(持续轮询),周期性发送探测数据段(zero window probe)询问窗口是否大于 0
  • 当接收方释放缓冲区后,回报新的窗口大小,发送方才会继续发送数据

🎯 四、流量控制 vs 拥塞控制(对比)

项目 流量控制 拥塞控制
控制对象 发送方 vs 接收方 发送方 vs 网络状态
目的 防止接收方缓存溢出 避免网络出现拥塞、丢包
依据 接收方给出的窗口大小(Window) 网络中丢包/延迟反馈
实现机制 滑动窗口 慢启动、拥塞避免、快重传等

🧠 五、图示理解(滑动窗口变化流程)

1
2
3
4
5
6
7
8
9
10
初始:     发送窗口 [0, 400)
发送200: SND.NXT 右移 → [200, 400)
ACK回200: 接收窗口 = 400 - 200 = 200

再发200: 接收方只处理100
ACK回100: 接收窗口 = 100

再发100: 接收窗口 = 0,暂停发送

轮询探测 → 接收方窗口恢复 → 恢复发送

📝 六、总结一句话:

TCP 流量控制通过接收方“反馈窗口大小”动态控制发送速率,配合滑动窗口机制,确保数据不会因“接收端处理能力不足”而丢失。


✅TCP 的滑动窗口(含示意与变量说明)


🧭 一、为什么需要滑动窗口?

如果每次发送数据都等待确认(ACK)后再发下一个,会导致效率低下,尤其在高延迟网络中(称为停等协议)。

为提高传输效率,TCP 引入了滑动窗口机制:

✅ 支持连续发送多个数据段,在等待确认的同时也能继续发送 ✅ 实现可靠传输流量控制高吞吐量


🧱 二、滑动窗口的基本概念

滑动窗口 = 一个字节流缓存区的大小控制机制

💡 有两个窗口:

  1. 发送窗口(Sender Window)
  2. 接收窗口(Receiver Window)

🧮 三、发送窗口(Sender Sliding Window)

发送窗口可分为 4 个区域,如下图逻辑所示:

1
2
3
4
		    ------  发送窗口 -------
|-----①-----|-----②-----|-----③-----|-----④-----|
已发送+确认 已发送未确认 可发送 不可发送
SND.UNA SND.NXT SND.WND
  • ① 已发送且收到 ACK 确认的数据
  • ② 已发送但未确认的数据
  • ③ 未发送但可以发送的数据(即窗口允许)
  • ④ 未发送也不可发送的数据(窗口之外)

📌 关键变量说明:

变量 含义
SND.UNA Unacknowledged,第一个未被确认的字节(已发未 ACK)
SND.NXT Next,第一个未发送的字节(下一个可以发送的)
SND.WND Window,发送窗口大小,由接收端 win 指定

发送窗口的右边界 = SND.UNA + SND.WND


📥 四、接收窗口(Receiver Sliding Window)

接收窗口可分为 3 个区域

1
2
3
4
            接收窗口 
|----①----|----②----|----③----|
已接收ACK 可接收区 不可接收区
RCV.NXT RCV.WND
  • ① 已成功接收并确认(RCV.NXT 之前的数据)
  • ② 可以接收但尚未接收的区域(可缓存区)
  • ③ 不可接收的数据(超出缓冲区大小)

📌 关键变量说明:

变量 含义
RCV.NXT Next,期望接收的下一个字节编号
RCV.WND 接收窗口大小,表示还能接收多少字节(来自 TCP 头部 win 字段)

🧠 五、滑动窗口的动态变化过程

  • 每当发送方发送数据 → SND.NXT 向右滑动
  • 每当接收方确认数据(ACK) → SND.UNA 向右滑动(窗口左边界变动)
  • 接收方会在 ACK 报文中更新自己的 RCV.WND,发送方用它调整自己的发送窗口

📉 六、TCP 滑动窗口的作用

功能 作用说明
流量控制 接收方通过 win 告诉发送方接收能力,避免缓冲区溢出
高效传输 发送方无需等待一个个 ACK,可以流水线式连续发送
可靠性保障 未确认数据仍保存在发送缓存,超时未确认可重传

✏️ 七、一句话总结

TCP 滑动窗口通过维护发送端与接收端窗口的动态范围,在保证数据可靠传输的同时实现了高效的数据流动与流量控制


✅ Nagle 算法与延迟确认


🧭 一、为什么需要 Nagle 算法和延迟确认?

问题背景

  • TCP 报文小数据传输时效率低:
    • TCP 头部:20 字节
    • IP 头部:20 字节
    • 小数据:可能只有几字节
    • ⏩ 有效负载比例极低,称为“小包问题

解决策略:

目的 方案
减少小数据包发送 👉 Nagle 算法
减少ACK 小包响应 👉 延迟确认

📦 二、Nagle 算法详解

作用:合并小数据包,减少小包数量,提高网络利用率。

基本思想

“任何时刻,最多只能有一个未被确认的小段在网络中。”

Nagle 算法规则:

  1. 若当前没有未确认的数据(即未收到 ACK) → 立即发送
  2. 若已有未确认的数据在等待确认 → 将新的小数据缓存在发送缓冲区
  3. 直到:
    • 之前数据收到 ACK 或
    • 当前数据累积达到 MSS 大小(最大报文段长度) 才会发送。

📌 示例比喻:

好比你打字发微信消息,每敲一个字就发一次网络请求太浪费,Nagle 算法就是“等你说一整句话或上次的消息已送达后再发”。

优点

  • 减少网络中小包数量
  • 降低带宽浪费

缺点

  • 会增加发送延迟(特别是在交互场景中,如 Telnet)

📬 三、延迟确认(Delayed ACK)详解

作用:合并 ACK 应答报文,减少 ACK 的数量,提高效率。

延迟确认策略

  1. 如果有返回数据要发 → ACK 会与数据一起发送
  2. 如果暂时没有数据可发 → 延迟 200ms 左右 看是否能与数据一起发 ACK
  3. 如果等不到数据,但又来了第二个数据包 → 立即 ACK

📌 示例比喻:

有人给你发来一条消息,如果你也要回复消息,你就顺便一起带上 ACK,否则等一等,看有没有一起回的机会。

优点

  • 降低网络中空 ACK 数量
  • 提高网络利用率

缺点

  • 若配合 Nagle 算法使用,可能导致发送与确认都延迟,产生显著性能问题(双重延迟 Deadly Delay

⚠️ 四、Nagle 算法 + 延迟确认 的组合问题

算法 行为
Nagle 发送端等 ACK 才继续发
延迟确认 接收端等 200ms 才发 ACK

📉 一起用可能出现的问题

  • 发送方发送小数据等待 ACK
  • 接收方等着发送数据再带 ACK
  • 双方互等,导致明显延迟 → 严重降低交互性能

🔧 五、使用建议

场景 建议
高交互应用(Telnet、SSH、游戏) ❌ 禁用 Nagle 算法(开启 TCP_NODELAY)
大数据批量传输 ✅ 可开启 Nagle 算法提高效率
应用层 ACK 已很频繁 ❌ 延迟确认可能带来副作用

✏️ 六、一句话总结

Nagle 算法通过合并小包发送,延迟确认通过合并 ACK 回应,两者都旨在提升网络效率,但一起使用可能导致发送与确认双重延迟问题,使用时要根据实际应用场景权衡。


✅ TCP 拥塞控制详细笔记


🧭 一、什么是拥塞控制?

拥塞控制(Congestion Control)是为了防止网络中的数据流过载,导致网络拥塞。流量控制是避免接收端缓存溢出,但它不能控制整个网络的状态。网络中的拥塞通常是由于多个主机共享带宽,导致丢包、延迟等问题。

拥塞控制的目标

  • 避免发送过多数据导致网络拥塞。
  • 通过调整数据发送速率来平衡网络负载。

📌 比喻:

就像一根水管,如果水流过大超过了水管的承载能力,水管就会爆裂(数据包丢失)。

拥塞控制的核心概念:

  • 拥塞窗口(cwnd):发送方控制发送数据量的变量,动态变化,反映网络的拥塞情况。
  • 发送窗口(swnd):由接收方控制,表示接收方的缓冲区可接受的数据量。
  • 接收窗口(rwnd):接收方告诉发送方的缓冲区剩余可接收的空间。

发送窗口接收窗口的最小值决定了能发送的最大数据量:

1
发送窗口 = min(cwnd, rwnd)

📉 二、拥塞控制的常见算法

TCP 使用以下几种算法来控制网络的拥塞情况:

  1. 慢启动(Slow Start)
  2. 拥塞避免(Congestion Avoidance)
  3. 拥塞发生(Congestion Occurrence)
  4. 快速恢复(Fast Recovery)

🏁 三、慢启动算法(Slow Start)

目的:在连接建立后,TCP 通过逐步增加拥塞窗口,探测网络的拥塞情况,避免过早地增加窗口导致网络拥塞。

原理

  • 初始值:cwnd = 1 MSS(一个最大报文段长度)
  • 每收到一个 ACK,cwnd 增加 1(单位是 MSS)。
  • 每轮次(RTT)发送的数据量呈指数增长(1 → 2 → 4 → 8),直到达到一个阈值(ssthresh)。

例子

  1. 初始:cwnd = 1
  2. 收到 1 个 ACK 后,cwnd 增加到 2(可以发送 2 个 MSS 数据)。
  3. 收到 2 个 ACK 后,cwnd 增加到 4,依此类推。

阈值(ssthresh)

  • 当 cwnd 达到阈值(ssthresh),进入拥塞避免阶段。

🚦 四、拥塞避免算法(Congestion Avoidance)

目的:在 cwnd 达到阈值后,通过线性增加拥塞窗口,避免由于过快增长导致的网络拥塞。

原理

  • 每收到一个 ACK,cwnd 增加 1/cwnd。
  • 每经过一个 RTT,cwnd 增加 1。

这是一种线性增长的方式,相比于慢启动的指数增长,它减少了网络过载的风险。

例子

  • 假设 cwnd 达到阈值 8,接收到 8 个 ACK 后,cwnd 增加 1(即 8 + 1 = 9),继续以线性方式增长。

🚨 五、拥塞发生(Congestion Occurrence)

当网络发生拥塞,数据包丢失时,TCP 会采取不同的处理方式:

  1. RTO 超时重传(Retransmission Timeout):超时重传机制。
  2. 快速重传(Fast Retransmit):发送方收到 3 个重复 ACK 后,立即重传丢失的数据包,而不等到 RTO。(这个就是cwnd减半了)

拥塞发生后的处理:

  • 慢启动阈值更新
    • ssthresh = cwnd / 2(减半)
    • cwnd = 1(重新开始慢启动过程)

⚡ 六、快速恢复算法(Fast Recovery)

当发生丢包时,如果收到 3 个重复 ACK,表示数据包丢失,但并不一定代表网络拥塞已经很严重。因此,TCP 不会回到慢启动阶段,而是使用快速恢复算法。

原理

  • cwnd = ssthresh / 2(减半)
  • ssthresh = cwnd
  • 进入快速恢复阶段:
    • cwnd = sshthresh + 3
    • 每收到一个重复 ACK,cwnd 增加 1。
    • 收到新的 ACK(说明丢失的数据包已经恢复),cwnd 恢复为 ssthresh

优点

  • 快速恢复:不需要重新进入慢启动,节省时间。

🧩 七、总结:TCP 拥塞控制的工作流程

阶段 行为 变化方式
连接建立 初始 cwnd = 1 MSS 指数增长(慢启动)
慢启动阶段 cwnd 增加,直到到达阈值(ssthresh) 指数增长
拥塞避免阶段 达到阈值后,cwnd 以线性方式增长 线性增长
网络拥塞发生 丢包,cwnd 减半,进入慢启动或快速恢复 重传、cwnd 降低
快速恢复阶段 收到重复 ACK 后,恢复数据包 cwnd 逐步增加

🔑 八、一句话总结

TCP 拥塞控制通过慢启动、拥塞避免、拥塞发生和快速恢复等算法,动态调整发送数据的速率,确保网络中的数据流不会导致网络过载,从而避免丢包和延迟等问题。


TCP 的重传机制

TCP 的重传机制是确保数据传输可靠性的关键部分,它在数据包丢失或未及时确认的情况下通过重发数据包来保证数据的完整性。TCP 支持几种不同的重传方式,包括超时重传、快速重传、带选择确认的重传(SACK)和重复 SACK。以下是详细说明:

1. 超时重传机制(Timeout Retransmission)

超时重传是 TCP 的一种核心机制,它基于时间驱动的方式来确保丢失的数据包能够被重发。

原理

  • 计时器:发送方在发送数据后启动一个计时器,等待接收方的确认(ACK)。如果在设定的超时时间内没有收到确认,发送方就会重新发送该数据包。
  • 重传超时(RTO):重传的时间(即超时时间)是根据网络的往返时间(RTT)动态计算的。TCP 使用一个算法来估算最佳的 RTO。

RTO 的计算

  • SRTT (Smoothed RTT):表示经过平滑处理的往返时间。

    • 计算公式:

      \[ \text{SRTT} = (1 - \alpha) \times \text{SRTT} + \alpha \times \text{RTT} \]

      其中,\(\alpha\) 是一个常量,通常为 0.125。

  • RTTVAR (RTT Variation):表示 RTT 的波动量。

    • 计算公式:

      \[ \text{RTTVAR} = (1 - \beta) \times \text{RTTVAR} + \beta \times |\text{RTT} - \text{SRTT}| \]

      其中,\(\beta\) 通常为 0.25。

  • RTO:最终的重传超时时间,计算公式为:

    \[ \text{RTO} = \text{SRTT} + \max(G, 4 \times \text{RTTVAR}) \]

    其中 \(G\) 是一个小常量,通常为 1 毫秒。

2. 快速重传(Fast Retransmit)

快速重传是另一种重传机制,它基于接收方的反馈来触发重传,而不是等待超时。

原理

  • 重复 ACK:当接收方接收到丢失数据包后的后续数据包时,它会向发送方发送重复的 ACK(即 ACK 序列号不变)。
  • 三次重复 ACK:当发送方收到三个相同的 ACK 时,认为某个数据包已经丢失,并立即重发该丢失的数据包。

优点

  • 减少等待时间:通过基于接收方的反馈触发重传,可以在网络延迟未到达超时限制前进行数据包的重传,从而加快恢复速度。

示例

假设发送方发送了 5 个数据包(Seq1 到 Seq5),但 Seq2 丢失:

  • 接收方接收到 Seq1 后,会返回 ACK2;
  • 接收方接收到 Seq3、Seq4、Seq5 时,依然返回 ACK2,因为它没有收到 Seq2;
  • 当发送方接收到三个 ACK2 后,知道 Seq2 丢失,于是立刻重发 Seq2。

3. 带选择确认的重传(SACK)

带选择确认的重传(Selective Acknowledgment,SACK)机制用于解决快速重传在某些情况下可能引起的重传不完全的问题。

原理

  • SACK 选项:接收方可以告诉发送方哪些数据包已经收到,哪些未收到。接收方通过返回一个包含序列号范围的 SACK 信息,指示哪些数据包是成功接收的。
  • 发送方重传:发送方根据接收到的 SACK 信息,只重传丢失的那些数据包,而不必重传所有数据。

优点

  • 减少带宽浪费:避免了不必要的重传,仅重传丢失的包。

示例

假设发送方发送了 1000 到 1050 序列的数据包,接收方收到 1000 到 1010 序列的数据包,并且 1011 到 1020 丢失。接收方通过 SACK 告诉发送方丢失的是 1011 到 1020 这一段,发送方根据这个信息只重传丢失的数据包。

4. 重复 SACK(D-SACK)

重复 SACK(Duplicate SACK,D-SACK)是 SACK 的一种扩展,主要用于标识并帮助发送方识别哪些数据包被重复接收。

原理

  • 重复接收:接收方可能由于 ACK 丢失等原因,重复接收某些数据包。在这种情况下,接收方通过 D-SACK 告诉发送方哪些数据包被重复接收了,帮助发送方判断是否存在丢包或重传错误。

优点

  • 改善重传行为:D-SACK 可以帮助发送方更准确地判断数据包的接收情况,从而优化流量控制和拥塞控制。

示例

如果发送方丢失了某些 ACK 报文,接收方可能会重复确认某些数据包的接收。此时,接收方返回 D-SACK 信息,指示发送方某些数据包是重复接收到的,帮助发送方纠正重传策略。

总结

TCP 的重传机制通过多种方式确保数据的可靠传输。超时重传解决了数据包丢失的问题,但可能引起较长的等待时间;快速重传和带选择确认的重传提高了丢包恢复的效率;而重复 SACK 机制则进一步优化了重传过程,避免不必要的重传和冗余。

TCP 粘包和拆包问题

TCP 是一个面向字节流的协议,这意味着它并不关心数据的边界。它将应用层的数据流转化为一系列的字节进行传输,但 TCP 协议本身并没有内建的数据分界机制,导致在数据传输中可能出现粘包拆包的问题。

1. 什么是 TCP 粘包和拆包?

  • 粘包(Stickiness / Packet Fusion):发生在多个小数据包被合并成一个大数据包发送的情况。即发送端的多个小数据包被 TCP 协议合并成一个大的包进行传输,接收端在接收时就会拿到一个大的数据块,而无法区分原来数据的边界。
  • 拆包(Packet Splitting):发生在一个大数据包被分割成多个小数据包进行发送的情况。即发送端发送的一个大数据包,因为 TCP 的缓冲区或最大报文段大小(MSS)限制,被拆分成多个小包传输。

2. 为什么会产生粘包和拆包?

TCP 粘包和拆包问题的原因主要与 TCP 协议的工作方式及其缓冲区管理机制有关:

  • 缓冲区大小:当发送的数据量小于 TCP 发送缓冲区的大小时,TCP 会将多个小的数据包合并到一起一次发送,这就会导致粘包的情况发生。
  • 接收方读取缓慢:接收方如果没有及时处理接收到的数据,就会导致多个数据包的内容粘在一起,造成粘包现象。
  • 数据包大小限制:当要发送的数据大于 TCP 发送缓冲区剩余的空间时,TCP 会将数据拆成多个包进行发送,这就会导致拆包现象。
  • MSS(最大报文段大小):当待发送的数据包大小超过 TCP 的最大报文段长度(MSS)时,数据会被拆分成多个小包进行传输。

3. 粘包和拆包的具体例子

  • 粘包示例:假设发送方需要传输数据包 A 和 B,发送方的发送缓冲区足够大,且 TCP 协议将 A 和 B 合并成一个数据包发送。接收方在接收时只接收到一个大数据块,但无法知道这个大数据块到底包含了 A 和 B 两个数据包的信息,接收方需要自行解析。
  • 拆包示例:假设发送方的数据包 C 比 TCP 发送缓冲区的大小要大,TCP 会将数据 C 拆分成多个包分别发送。接收方在接收时会收到多个小的数据块,每个数据块都可能是 C 的一部分,接收方需要进行数据重组才能得到完整的数据。

4. 如何解决 TCP 粘包和拆包问题?

为了避免粘包和拆包问题带来的困扰,常用的解决方案有:

4.1 发送端固定长度封装

发送端可以规定每个数据包的大小固定。例如,每次发送的数据都是 1024 字节,这样接收方就可以很容易地确定每个包的边界。该方法适用于发送的数据量较为均匀且能够预知长度的情况。

4.2 使用特殊字符作为分隔符

在每个数据包的末尾增加一个特殊字符或标志(例如,\n| 或者其它控制字符),接收方通过查找这个特殊字符来判断数据包的结束位置。该方法常见于文本协议中,如 HTTP 协议中的换行符用于标识请求头的结束。

4.3 使用固定头部和长度字段

一种常见的做法是将数据分为两部分:头部和数据体(内容)。头部有固定的长度,并包含一个字段,表示数据体的长度。这样,接收方就可以通过读取头部信息得知数据体的大小,从而正确地解析出完整的数据包。

  • 例如,头部可能包含一个整数值,表示数据体的字节长度。接收方首先读取这个整数,得知接下来需要读取多少字节的数据体。

示例格式:

1
2
3
4
+---------+-----------+
| Header | Data Body |
| (4 bytes) | (n bytes) |
+---------+-----------+

这种方式可以确保接收方准确地识别出数据包的边界。

4.4 使用应用层协议(如 HTTP 或 WebSocket)

高层协议可以在数据传输时提供一定的封装,避免了原始 TCP 流的粘包和拆包问题。比如 HTTP 协议中的“Content-Length”字段就是告诉接收方数据包的长度,WebSocket 协议则通过消息帧头和负载数据来实现可靠的分包和合并。

总结

  • 粘包拆包是由于 TCP 协议本身是面向字节流的,且缺乏固有的消息边界机制,因此数据的边界可能会丢失或混合。
  • 解决粘包和拆包问题的常见方法有固定长度数据封装、使用特殊字符作为分隔符和在头部字段中注明数据长度等。
  • 高层协议如 HTTP、WebSocket 等通常在传输中引入了自己的边界识别方式,从而避免了低层传输中的粘包和拆包问题。

一个 TCP 连接可以发送多少次 HTTP 请求?

这个问题的答案取决于所使用的 HTTP 协议版本。不同版本的 HTTP 协议在连接复用和请求处理上有不同的机制和特点。

1. HTTP/1.0:每个请求一个 TCP 连接

  • 每个请求-响应建立一个新的 TCP 连接:在 HTTP/1.0 中,每发送一次 HTTP 请求,客户端和服务器都会建立一个新的 TCP 连接。
  • 无法复用连接:每次完成一个请求的响应后,连接就会关闭。这意味着每一个 HTTP 请求和响应都需要通过建立和关闭新的连接来完成,效率相对较低。

2. HTTP/1.1:持久连接与管道化

  • 持久连接(Persistent Connection):HTTP/1.1 引入了持久连接的概念,允许在一个 TCP 连接上发送多个请求和响应。默认情况下,HTTP/1.1 会在 Connection: keep-alive 头部中启用持久连接,从而避免了为每个请求都建立新的 TCP 连接。
    • Connection: keep-alive:告诉服务器和客户端不要在发送一个请求-响应对后关闭连接,连接会保持打开,直到明确请求关闭。
  • 管道化(Pipelining):HTTP/1.1 还支持请求管道化,允许客户端在等待前一个请求的响应时,继续发送多个请求。这意味着客户端可以提前发送多个请求,而不必等待每个请求的响应。
    • 然而,虽然 HTTP/1.1 支持管道化,但许多服务器和代理并没有完全实现这一功能。由于请求和响应必须按照发送的顺序处理,可能会出现队头阻塞(Head-of-line blocking)的问题,影响性能。

3. HTTP/2:多路复用与连接复用

  • 多路复用(Multiplexing):HTTP/2 在同一个 TCP 连接上引入了多路复用机制,允许多个请求和响应同时进行传输。每个请求和响应都被分割成独立的帧,并通过流(stream)进行并行传输。这样,客户端和服务器可以在同一连接上同时处理多个请求,而不会受到队头阻塞的影响。
  • 连接复用:与 HTTP/1.1 不同,HTTP/2 不需要像 HTTP/1.1 那样依赖 Connection: keep-alive,它自然支持在单一连接上同时处理多个请求和响应。HTTP/2 的多路复用机制显著提高了并发性能,减少了延迟和网络资源的浪费。

总结

  • HTTP/1.0:每个请求都需要建立新的 TCP 连接,因此每个 TCP 连接只能处理一个 HTTP 请求。
  • HTTP/1.1:支持持久连接,一个 TCP 连接可以发送多个 HTTP 请求和响应,默认启用 Connection: keep-alive 以维持连接。
  • HTTP/2:通过多路复用,一个 TCP 连接可以同时处理多个 HTTP 请求和响应,大幅提高了并发性能和资源利用率。

因此,一个 TCP 连接能发送多少次 HTTP 请求,在 HTTP/1.0 中只能发送一次,而在 HTTP/1.1 和 HTTP/2 中,单个 TCP 连接可以发送多个请求和响应。HTTP/2 在这一点上提供了最强大的支持。

TCP 和 UDP 的区别

1. 连接方式

  • TCP(传输控制协议):TCP 是面向连接的协议,在数据传输开始之前需要先建立连接,传输完成后再断开连接。这一过程通过“三次握手”和“四次挥手”来实现。
  • UDP(用户数据报协议):UDP 是无连接的协议,数据传输之前不需要建立连接,传输完数据后也不需要断开连接,直接以数据报形式发送。

2. 可靠性

  • TCP:保证数据的可靠传输,通过确认机制、重传机制、流量控制等来确保数据的完整性。即使数据包丢失,TCP 会进行重传,直到接收方确认接收到数据。
  • UDP:不保证数据的可靠传输,数据包可能会丢失、重复或乱序。UDP 只是负责发送数据,不会进行重传,也不进行流量控制。

3. 数据传输方式

  • TCP:数据传输是有序的,接收方会按照发送顺序处理数据。如果有数据丢失,TCP 会自动重发。
  • UDP:数据传输是无序的,不保证数据包的顺序,接收方需要自己处理数据的顺序问题。

4. 性能

  • TCP:由于需要建立连接、确认收发和重传机制,TCP 的开销相对较大,传输速度较慢。
  • UDP:没有连接管理、确认机制和重传机制,因此 UDP 的传输速度较快,延迟较低。

5. 适用场景

  • TCP:适用于对数据传输可靠性要求高的场合,如网页浏览、电子邮件、文件传输(FTP)、远程控制、数据库连接等。
  • UDP:适用于对实时性要求高、对数据丢失有一定容忍度的场合,如实时语音、视频通话、在线游戏、广播通信等。

TCP 和 UDP 应用场景

1. TCP

适用于对数据准确性要求较高的场合,例如:

  • 网页浏览:浏览器使用 HTTP 协议时,TCP 用于可靠地传输网页内容。
  • 文件传输(FTP):需要确保文件完整传输,TCP 提供可靠传输机制。
  • 电子邮件:邮件的传输要求完整性,TCP 提供保证。
  • 远程控制:例如 SSH 和 Telnet,都使用 TCP 来保证数据可靠传输。

2. UDP

适用于对速度要求较高、可以容忍一定数据丢失的场合,例如:

  • 在线视频:视频播放可以容忍少量丢包,但需要低延迟,因此 UDP 更适合。
  • 语音通话(VoIP):语音通话需要高实时性,使用 UDP 以保证低延迟。
  • 实时游戏:大多数网络游戏使用 UDP,因为游戏对延迟敏感,允许丢失少量数据包。
  • 广播通信:UDP 支持一对多或多对多的广播模式,适用于广播服务。

设计 QQ 中的网络协议

1. 登录功能

  • 使用 TCP + SSL/TLS 协议:登录过程涉及敏感信息(如账号、密码),需要保证数据的安全性。TCP 协议确保数据可靠传输,而 SSL/TLS 提供加密保证,确保信息的安全性。

2. 消息传递

  • 实时消息传递:对于消息的传输,尤其是语音和视频通话,UDP 协议非常合适。UDP 具有较低的延迟,可以保证实时性,适合对速度要求较高的应用场景。

3. 保证消息不丢失

  • TCP 的保证:TCP 协议会自动进行重传,确保数据包的可靠性。
  • UDP 的保证:由于 UDP 不提供可靠性保证,应用层可以实现自定义的重传机制。
    • 例如,发送方在发送数据时附带序列号,接收方根据序列号判断数据包的顺序,并反馈确认信息。如果接收方发现数据包丢失或顺序不对,发送方可以根据接收到的确认信息进行重传。

4. 消息持久化

  • 消息持久化:可以将消息保存在服务器或本地数据库中,防止由于网络中断导致的消息丢失。即使网络恢复,用户仍然能够收到未到达的消息。

面试题回顾

  • TCP 和 UDP 的区别:如上所述,TCP 是面向连接、可靠的,而 UDP 是无连接、不可靠的协议。
  • QQ 协议设计:基于应用场景,QQ 使用 TCP + SSL/TLS 来保证登录过程的安全性,使用 UDP 来传输实时消息(如语音视频),并通过应用层的重传机制来确保 UDP 消息的不丢失。

总结

  • TCP 适用于需要高可靠性和数据完整性的场合。
  • UDP 适用于需要低延迟和高性能的实时应用,但对数据丢失有一定容忍度。

UDP 协议为什么不可靠?

UDP 是不可靠的,因为它在数据传输过程中不执行以下几项功能:

1. 不保证消息交付

  • UDP 不提供确认机制。接收方收到数据包后不会发送确认消息给发送方,因此发送方无法得知数据是否成功送达。
  • 如果在传输过程中数据包丢失,UDP 不会进行重传,也不会等待超时重新传输丢失的数据。

2. 不保证交付顺序

  • UDP 没有数据包排序的机制。如果发送的多个数据包顺序错乱,接收方不能自动重排。接收到的数据包可能是乱序的。
  • UDP 也不会发生队首阻塞问题,即如果某些包丢失或未按顺序到达,不会导致后续数据包无法到达。

3. 不跟踪连接状态

  • UDP 是无连接协议,它不需要建立连接,也不维护连接的状态。发送方直接将数据包发送到目标主机,不关心接收方是否准备好接收数据。
  • 因此,UDP 不会像 TCP 一样为每个连接创建和维护连接状态(如三次握手等)。

4. 不进行拥塞控制

  • UDP 协议没有内建的流量控制和拥塞控制机制。网络中的数据传输可能会发生网络拥堵,导致丢包或延迟,但 UDP 不会自动调整数据发送速率来避免拥塞。
  • 这意味着 UDP 的使用者(通常是应用层)需要自己处理拥塞和流量控制问题。

** DNS与UDP?**

1. DNS 的应用场景

  • DNS(Domain Name System,域名系统)主要用于将域名解析为 IP 地址。它的一个关键特点是要求高效、快速的查询和响应。
  • 大多数情况下,DNS 查询的数据包大小较小,通常在 512 字节以内,因此 UDP 是合适的选择。

2. 为什么使用 UDP 进行查询?

  • 无需建立连接:UDP 是无连接的协议,查询一个域名时,客户端直接向 DNS 服务器发送请求,服务器响应,不需要像 TCP 那样建立连接。这样可以大大提高查询速度。
  • 响应时间短:由于 UDP 没有连接建立、确认和重传等机制,响应速度较快,适合 DNS 这种需要高效处理的应用。
  • 传输效率高:UDP 相较于 TCP 更轻量,不需要复杂的握手和拆包操作,对于大多数 DNS 查询来说,UDP 是理想选择。

3. 为什么某些情况下 DNS 使用 TCP?

  • 区域传送:DNS 服务器之间的区域传送(即主域名服务器和辅助域名服务器之间的数据同步)使用 TCP,因为传输的数据量较大,超过了 UDP 可传输的最大大小(512 字节)。TCP 支持更大的报文长度,可以保证数据传输的可靠性。
  • 保证数据完整性:TCP 协议提供了数据传输的可靠性机制,包括数据包确认、重传等,可以确保数据的完整性,适合用于同步较大的数据。

4. UDP 的可靠性处理

  • 尽管 UDP 本身不保证可靠传输,但 DNS 协议会通过设置超时和重传机制来弥补这一点。例如,如果客户端没有在超时期限内收到响应,它会重新发送请求。
  • DNS 服务器和客户端自己管理超时、重传等问题,确保尽可能可靠地完成解析。

总结:

  • DNS 查询:使用 UDP 以提高效率,减少延迟,适用于小数据量的快速查询。
  • 区域传送:使用 TCP,确保大数据量的可靠传输。

UDP 和 TCP 的使用根据场景的需求和数据量的大小来选择,以确保在不同的应用中能够达到最佳的性能和可靠性。