HuiBlog

TCP详解

Design challenge

1.Internet is federated

2.handle failure at sacle

3.asychrony

4.diverse

5.keep evolving

Layers of network

  1. physical layer: 信息传递的物理媒介
  2. link layer : 直接在物理layer上连接的计算机组成的局域网.
  3. Internet : 由所有局域网相连组成的互联网.

Layer 3不保证packet一定送到目标地址. packet可能会lost, reordered, corrupted.并不会告知错误.

所以需要layer 4 transport,保证packet送到且无误.

Layer 5,6 被废弃了.

layer 7 应用层. 用户直接交互.

header

包含一些元数据, 例如发送接受者地址, packet size.

packet传输过程

假设host A 传packet给host B. 会经过以下过程:

旅程开始:在 Host A (发送者)

数据在这里被 “封装” (Encapsulation),像套娃一样一层包一层。

  • Layer 7 (应用层):
    • 作用: 准备要发送的数据。
    • 数据: "Potato." (这是一个 HTTP GET 请求)
  • Layer 4 (传输层):
    • 作用: 管理端到端的连接,分配端口号。
    • 操作: 加上一个 TCP 头部
      • Source Port (源端口): 49152 (一个随机的高端口号)
      • Destination Port (目标端口): 80 (HTTP的默认端口)
    • 结果: [ TCP 头部 | "Potato." ],这被称为一个 "Segment" (数据段)
  • Layer 3 (网络层 / Internet):
    • 作用: 加上全局的“最终地址”。
    • 操作: 加上一个 IP 头部
      • Source IP (源IP): IP_A (192.168.1.10)
      • Destination IP (目标IP): IP_B (172.16.5.50)
    • 结果: [ IP 头部 | TCP 头部 | "Potato." ],这被称为一个 "Packet" (数据包)
    • A的思考: "目标 IP_B (172.16.5.50) 不在我的本地网络 (192.168.1.0),我必须把它发给我的网关(Gateway),也就是 Router 1 (192.168.1.1)。"
  • Layer 2 (链路层 / Link):
    • 作用: 加上“下一跳”的“本地地址”。
    • 操作: 加上一个 Link 头部 (比如以太网帧头)。
      • Source MAC (源MAC): MAC_A (Host A 自己的 MAC)
      • Destination MAC (目标MAC): MAC_R1_A (Router 1 的 MAC)。
      • (注意:Host A 通过 ARP 协议找到 IP_R1_A 对应的 MAC 地址是 MAC_R1_A)
    • 结果: [ Link 头部 | IP 头部 | TCP 头部 | "Potato." ],这被称为一个 "Frame" (数据帧)
  • Layer 1 (物理层):
    • 作用: 把数据帧转换成电子信号。
    • 操作: 把这个"Frame"的 0 和 1 序列变成电信号(或光信号、无线电波),通过网线发送出去。

第一站:在 Router 1

路由器在这里进行 “解封装” 然后 “重新封装”

  • Layer 1: 接收电信号,将其转换回 0 和 1 的 "Frame"。
  • Layer 2:
    • 作用: 检查这个“Frame”是不是给我的。
    • 操作: 查看 "Frame" 的 Destination MAC“是 MAC_R1_A!这是发给我的。”
    • 操作: “拆掉” (Decapsulate) L2 的 Link 头部,把里面的 "Packet" 取出来,交给 L3。
  • Layer 3:
    • 作用: 决定下一步该往哪走(路由)。
    • 操作: 查看 "Packet" 的 Destination IP“是 IP_B (172.16.5.50),这不是给我的。”
    • 操作: 查询自己的路由表 (Routing Table)
    • 路由决策: “根据我的路由表,要去 172.16.5.0 这个网络,我必须把包从我的接口2 (IP_R1_B) 发给 Router 2 (IP_R2_A / 10.0.0.2)。”
    • 操作: 把这个原封不动的 "Packet" 向下交给 L2。
  • Layer 2 (再次):
    • 作用: 为下一跳(Router 1 -> Router 2)封装一个新的 "Frame"。
    • 操作: 加上一个全新的 Link 头部
      • Source MAC: MAC_R1_B (Router 1 自己的出口 MAC)
      • Destination MAC: MAC_R2_A (Router 2 的 MAC)
    • 结果: [ 新 Link 头部 | IP 头部 | TCP 头部 | "Potato." ]
  • Layer 1 (再次): 把这个新的 "Frame" 转换成信号,通过连接 Router 2 的网线发出去。

第二站:在 Router 2

操作与router 1相似, 只是将packet发给host B

终点站:在 Host B (接收者)

数据在这里被 “解封装” (Decapsulation),一层层拆开。

  • Layer 1: 接收电信号,转换回 "Frame"。

  • Layer 2:

    • 作用: 检查这个“Frame”是不是给我的。
    • 操作: 查看 Destination MAC“是 MAC_B!这是发给我的。”
    • 操作: “拆掉” L2 头部,把 "Packet" 取出来,交给 L3。
  • Layer 3:

    • 作用: 检查这个“Packet”是不是给我的。
    • 操作: 查看 Destination IP“是 IP_B!这是发给我的。”
    • 操作: “拆掉” L3 头部,把 "Segment" 取出来,交给 L4。
  • Layer 4:

    • 作用: 检查这个“Segment”该给哪个应用程序。
    • 操作: 查看 Destination Port“是 80 端口!这是给我的 Web 服务器的。”
    • 操作: “拆掉” L4 头部,把“数据”取出来,交给 L7。
  • Layer 7:

    • 作用: 处理数据。
    • 操作: Web 服务器收到了数据:"Potato."

    design principles

    image.png

    image.png

image.png

路由

解决的问题: A要给B发消息, 他怎么知道发给哪个路由器中转? 以及多条路径的选择.

互联网是由多个网络互联构成的网络, 因此所用的路由协议并不相同. 但是一个网络内部使用的路由协议是相同的. 被称为IGP. 网络之间的路由协议称为BGP.

对于IGP, 实现路由的算法通常是链路状态算法:

这是现代IGP(内部网关协议)的主流算法。它的工作原理是“构建完整的地图”。

  • 代表协议OSPF (Open Shortest Path First)
  • 核心算法Dijkstra (迪杰斯特拉) 算法,也叫 SPF (最短路径优先)
  1. 步骤 1:建立邻居关系 (Adjacency)
    • 路由器通过发送 Hello 包来发现直连的邻居。
    • 它们交换参数,建立“邻居关系”,同意互相交换信息。
  2. 步骤 2:构建链路状态数据库 (LSDB)
    • 泛洪 (Flooding):每个路由器都会创建一个“链路状态通告 (LSA)”包。
    • LSA包的内容是:“我是路由器A,我的状态是:[我与B相连,成本10], [我与C相连,成本5], [我连着网络X,成本20]...”。
    • 这个LSA包会被“泛洪”给网络中(同一区域内)的所有其他路由器。
    • LSDB:每个路由器都会收集来自所有路由器的LSA,把它们存储在一个“链路状态数据库 (LSDB)”中。
    • 关键点:在收敛后,区域内每台路由器的LSDB是完全一致的
  3. 步骤 3:运行 Dijkstra (SPF) 算法
  4. 步骤 4:构建路由表
    • Dijkstra算法的产出是一个“最短路径树”,这个树显示了“我(树根A)”到网络中所有其他节点的最短路径。
    • 路由器A会解析这个树,并把结果(“要去D,下一跳是C,从G0/1口出”)安装到自己的路由表中,用于数据转发。

可以看出IGP协议路由表的计算复杂, 如果路由器太多,负担很大.因此内网也化分为一个个区域, 在区域中用IGP, 区域之间使用类似BGP协议.

路径矢量 (Path-Vector) 算法

这是一种完全不同的思路,用于EGP(外部网关协议)。它不关心“成本”或“速度”,它关心“策略”和“无环路”。

算法实现:

BGP的“算法”是一个决策流程,而不是一个数学计算。

  1. 信息交换

    • BGP 路由器之间交换的是“路径”信息,而不是成本。
    • 核心属性是 AS_PATH (AS路径):这是一个列表,记录了要想到达某个网络,需要经过的“自治系统 (AS)”的编号。
    • 示例:AS 1 告诉 AS 2:“要去网络X,路径是 [AS 1, AS 3, AS 4]。”
  2. 核心算法 1:环路避免

    • 当 AS 2 收到这条通告时,它会检查 AS_PATH 列表。
    • 如果 AS 2 在列表中看到了自己的编号 (AS 2),它就知道这是一个环路,立即丢弃这条路由。
    • 这是“路径矢量”算法的根本机制
  3. 核心算法 2:最佳路径选择

    • 一个路由器(比如AS 2)可能会从多个邻居(AS 1, AS 5, AS 6)收到关于同一个目的地(网络X)的多条路径。
    • 必须只选择一条“最佳”路径放入路由表。
    • 它会按照一个严格的、顺序的规则列表(这就是“算法”)来决策:
      1. (检查有效性...)
      2. 规则 1:优先选择 WEIGHT (权重) 最高的路径 (这是一个本地配置值,仅在本路由器有效)。
      3. 规则 2:如果权重相同,优先选择 LOCAL_PREFERENCE (本地偏好) 最高的路径 (这个值会在整个AS内部共享,用于决定“出口”)。
      4. (检查是否本地始发...)
      5. 规则 3:如果以上都相同,优先选择 AS_PATH 最短的路径 (这是默认的“最短”标准)。
      6. 规则 4:如果 AS_PATH 长度也相同,比较 ORIGIN (起源) 类型。
      7. 规则 5:如果 ORIGIN 也相同,选择 MED (多出口鉴别器) 值最低的。
      8. (...等等,还有很多步...)
      9. 最终规则:如果所有都一样,选择 BGP Router-ID 最低的那个邻居发来的路径。
    • 这个冗长的列表就是BGP的“算法”——它是一种基于策略属性的决策树

    IP协议与路由协议的关系

    IP 协议定义了地址的格式和层次结构。一个设备(机器)获得的IP地址(例如 192.168.1.100),让它在网络中有了唯一的“逻辑坐标”. 并定义包裹的格式(数据头). 并根据路由表转发这些包裹.

    路由协议(如OSPF)在路由器之间运行,它们互相通告的是IP地址的可达性,并以此计算并构建路由表。

Transport

传输层协议设计理念

对于应用层, 传输层要将数据的传输抽象成一个端到端的连接, 应用层不考虑数据是怎么被传输的, 只直到与服务器建立连接后, 发送的数据一定会完好的, 顺序不变的到达服务器. 以及对于多个应用, 应该可以同时建立网络连接

有这么几个问题:1) 保证数据发送一定送到且没有损坏. 2) 保证数据发送的顺序不变 3) 对于机器上的多个应用允许同时多个连接.(多路复用). 4. congestion control. 避免传输的包太多,在网络中阻塞 5.flow control 依然是包一次传输太多, 接收者缓冲区不够.

此外,在解决以上问题的同时, 要做到尽量快的速度, 以及占用尽量少的带宽. 另外也有对速度要求很高但是可靠性不高的需求.

TCP协议

single packet reliability protocol

If you are the sender: Send the packet, and set a timer. If no ack arrives before the timer goes off, re-send the packet and reset the timer. Stop and cancel the timer when the ack arrives.

If you are the recipient: If you receive the uncorrupted packet, send an ack. (You might send multiple acks if you receive the packet multiple times.)

The core ideas in this example will apply to later protocols as well: checksums (for corruption), acknowledgements, re-sending packets, and timeouts.

Note that this protocol guarantees at-least-once delivery, since duplicates may exist.

可靠传输多个数据包

一、基本问题与解决方案

1. Stop-and-Wait协议(停止等待)

  • 方法: 发送包i后,等待ACK才发送包i+1
  • 优点: 简单可靠
  • 缺点: 太慢!每个包至少需要1个RTT

2. 改进:并行发送

  • 问题:
    • 一次发一个太慢
    • 一次全发会压垮网络
  • 解决: 窗口机制 - 限制同时在网络中(in-flight)的包数量为W

二、窗口大小(W)的确定

核心公式

W × 包大小 = RTT × 瓶颈带宽(B)

三个目标及对应窗口:

1️⃣ 充分利用带宽(填满管道)

W = RTT × B / 包大小

  • 例子: RTT=1秒, B=8Mbps, 包大小=100字节
    • W = 1秒 × 8Mbps / 100字节 = 10,000个包
  • 目标: 让发送方在整个RTT期间持续发送,不闲置

2️⃣ 流量控制(不压垮接收方)

W ≤ 接收方广告窗口(Advertised Window)

  • 原因: 接收方需要缓存乱序到达的包,内存有限
  • 机制: 接收方在ACK中告知剩余缓冲区大小

3️⃣ 拥塞控制(不压垮网络)

W ≤ 拥塞窗口(Congestion Window, cwnd)

  • 原因: 多个连接共享带宽,需要公平分配
  • 机制: 通过拥塞控制算法动态计算

最终窗口大小

W = min(拥塞窗口, 接收方广告窗口)

注: 实践中忽略"充分利用带宽"这个值,因为难以准确获知,且总是≥拥塞窗口


三、更智能的确认机制(ACK)

对比三种ACK策略:

| 策略 | 内容 | 优点 | 缺点 | | --- | --- | --- | --- | | 单独ACK | 每个ACK对应一个包 | 清晰明确 | ACK丢失会导致不必要的重传 | | 完全信息ACK | 列出所有已收到的包 | 信息完整,容错性好 | ACK可能变得很长 | | 累积ACK | 只报告"收到所有≤N的包" | 固定长度,不会膨胀 | 信息模糊,不知道哪些乱序包已到达 |

TCP实际使用: 累积ACK

示例:
- 收到包1,2,4,5
- ACK显示: "所有≤2的包都收到了"
- 不包含4,5的信息(因为3缺失,不连续)


四、提前检测丢包(快速重传)

不等超时,提前检测丢包:

规则: 如果在缺失包之后收到K个后续包的ACK,则认为该包丢失

在不同ACK机制下的表现:

1. 单独ACK: 清晰

缺包5,收到ACK: 6, 7, 8
→ 3个后续ACK到达,判定包5丢失

2. 完全信息ACK: 清晰

ACK显示: "≤4", "≤4,plus 6", "≤4,plus 6,7", "≤4,plus 6,7,8"
→ 明确看到3个后续包,判定包5丢失

3. 累积ACK: 模糊但可行

ACK显示: "≤4", "≤4", "≤4", "≤4" (重复ACK)
→ 收到3个重复ACK(共4个"≤4"),判定包5丢失

问题: 累积ACK在多包丢失时模糊

场景: 包3和5都丢失,窗口W=6, K=3
- 收到重复ACK后知道有包丢失
- 但不确定是哪些包丢失
- 发送方需要推测应该重传哪个包


五、关键要点

  1. 窗口机制是核心: 平衡速度与网络负载
  2. 窗口大小由三个因素决定: 带宽、接收方能力、网络拥塞
  3. 累积ACK是工程权衡: 简单高效,但牺牲了部分信息精确度
  4. 快速重传: 比超时重传快得多(微秒级 vs 秒级)
  5. 多包丢失时的模糊性: 累积ACK的固有缺陷,但整体效益仍优于单独ACK

TCP实现


一、从概念到实现:字节流抽象

1.1 核心转变

  • 之前:概念层面讨论"发送packet"
  • 现在:应用层提供的是连续字节流,不是预制的packet
  • 实现挑战:需要将所有TCP概念(序列号、窗口大小等)从"packet视角"转换为"字节视角"

1.2 TCP Segment的产生

定义:TCP segment是TCP实现层面的数据单元

形成过程:

  1. 发送方的TCP实现从字节流中逐字节收集数据
  2. 将字节放入一个TCP segment中
  3. 当segment达到**最大segment大小(MSS)**时,发送该segment
  4. 开始填充新的segment

处理不满载的情况:

  • 问题:如果发送数据量小于MSS,segment会一直等待
  • 解决:每次开始填充新segment时启动计时器
  • 机制:计时器到期时,即使segment未满也发送

1.3 数据封装流程

应用层字节流 ↓ TCP实现收集字节 → 形成TCP segment ↓ 添加TCP header(序列号、端口号等) ↓ 传递给IP层 ↓ 添加IP header ↓ 通过网络发送 (形成TCP/IP packet)

术语:

  • TCP/IP packet = IP header + TCP header + TCP segment data
  • 等价于:payload是TCP数据的IP packet

1.4 MSS的计算

MSS = MTU - IP header大小 - TCP header大小 典型值: 1460字节 = 1500字节 - 20字节 - 20字节

原理:

  • MTU限制了IP packet的总大小
  • TCP segment必须为IP/TCP header留出空间
  • MSS是TCP能承载的纯数据大小

二、序列号机制(Sequence Numbers)

2.1 字节编号系统

  • 不是给segment编号,而是给字节流中的每个字节编号
  • 每个segment的序列号 = 该segment中第一个字节的编号
  • 接收方通过序列号重组segment

2.2 初始序列号(ISN)

机制:

  • 每个字节流从一个随机选择的ISN开始
  • 第一个字节编号 = ISN+1
  • 第二个字节编号 = ISN+2
  • 依此类推

示例:

`ISN = 50 字节编号:51, 52, 53, 54... 某segment包含字节140-219:

  • 序列号 = 140
  • 包含80个字节`

2.3 确认号(Acknowledgement Number)

含义:

  • "我已收到所有字节,直到但不包括这个编号"
  • 等价于:"我期待的下一个字节编号"
  • 使用累积确认模型

数学关系:

`假设packet:

  • 首字节序列号 = X
  • 包含B个字节
  • 字节范围:X, X+1, X+2, ..., X+B-1

如果正常接收:

  • 确认号 = X+B (下一个期待的字节)

如果丢失:

  • 确认号 = 某个小于X+B的数字(累积确认特性)`

2.4 为什么ISN是随机的

历史原因(已过时):

  • 防止连接崩溃后序列号混淆
  • 场景:sender崩溃重启,如果都从0开始,receiver无法区分新旧连接

现代原因(主要):

  • 安全性 - 防止序列号预测攻击
  • 如果ISN可预测,攻击者可以:
    • 推断出正确的序列号
    • 发送伪造的packet,看起来像是来自合法发送方
  • 随机ISN使攻击者难以猜测序列号

三、TCP状态管理

3.1 状态存储位置

  • 状态维护在终端主机上实现TCP的地方
  • 不在网络中存储状态

3.2 发送方需要维护的状态

  1. 未确认的字节:哪些字节已发送但未被确认
  2. 各种计时器:
    • 何时发送未满载的segment
    • 何时重传字节

3.3 接收方需要维护的状态

  • 乱序字节:已收到但还不能交付给应用层的字节

3.4 连接的概念

因为需要维护状态:

  • 每个字节流称为连接(connection)
  • TCP是面向连接的协议(connection-oriented)

与Layer 3的区别:

  • Layer 3:每个packet可独立考虑
  • TCP:双方必须先建立连接并初始化状态,才能发送数据

连接管理:

  • 需要机制来建立连接
  • 需要机制来拆除连接(释放两端的状态内存)

五、TCP握手(Three-way Handshake)

5.1 握手的目的

  • TCP是面向连接的,连接必须显式创建
  • 字节流从随机选择的ISN开始
  • 全双工连接有两条字节流,需要两个ISN(每个方向一个)
  • 握手目标:双方协商两个方向的ISN

5.2 三次握手流程

第一步:SYN

  • A → B发送SYN消息
  • 包含A的ISN(在序列号字段中)
  • 含义:"A→B方向的数据将从这个ISN开始计数"

第二步:SYN-ACK

  • B → A发送SYN-ACK消息
  • 序列号字段:包含B的ISN(B→A方向的数据从此开始)
  • 确认号字段:确认收到A的ISN(值为A的ISN+1)

第三步:ACK

  • A → B发送ACK消息
  • 确认号字段:确认收到B的ISN(值为B的ISN+1)

如果没有建立连接就发送数据包?

服务器发现包来自未建立连接的IP和端口, 丢弃数据包, 并发送RST.

正常客户端会发现RST, 连接断开.

但是恶意程序如果一直发送包, 不管RST, 会对服务器造成性能上的影响(需要处理解析包以及发送RST.占用CPU,以及网络带宽)

这也是DDos攻击的原理(其通常用SYN Flood).

六、结束连接

6.1 正常结束:FIN packet

机制:

  1. 一方发送完数据后,发送FIN packet
    • 含义:"我不会再发送数据,但继续接收你的数据"
    • 此时连接进入半关闭状态
    • 这个FIN会像普通packet一样被ACK
  2. 另一方最终也发送完数据,发送自己的FIN packet
  3. 当第二个FIN被ACK后,连接完全关闭

6.2 强制结束:RST packet

使用场景:

  • 需要突然终止连接
  • 不需要对方同意
  • 单方面结束连接

机制:

  • 发送RST packet
  • 含义:"我不会再发送或接收任何数据"
  • 不需要被ACK
  • 发送RST后立即拆除连接

典型使用情况:

  1. 主机遇到错误,无法继续收发packet
  2. 任何在传输中的数据都会丢失(如果主机崩溃并丢失状态)
  3. 如果对方继续发送数据,会重复发送RST packet试图终止连接

安全问题:

  • 攻击者可以伪造并注入RST packet
  • 导致整个连接被终止
  • 这是一种审查连接的攻击手段

七、滑动窗口(Sliding Window)

8.1 从packet窗口到字节窗口

之前(packet视角):

  • 窗口 = 任意时刻可以在传输中的packet数量

现在(字节视角):

  • 滑动窗口 = 任意时刻可以在传输中的最大连续字节数

8.2 连续性限制

关键差异:

  • Packet窗口:允许非连续packet在传输中(如packet 5,7,8)
  • 字节窗口:必须连续,不能有间隙
  • 这个限制形成了字节流中的一个"窗口(范围)"

8.3 窗口的边界

左边界:

  • 第一个未确认的字节(由接收方的ACK号确定)

右边界:

  • 从左边界开始,接下来的W个字节
  • W = 窗口大小

可发送范围:

  • 左边界到右边界之间的字节可以在传输中

可视化:

字节流: ... | 已确认 | ←----- 窗口(W字节) -----→ | 不能发送 | ... ↑ ↑ 左边界 右边界 (第一个未确认字节)

8.4 窗口移动规则

重要限制:

  • 即使窗口内的某些中间字节已被确认
  • 仍然不能发送超出右边界的字节
  • 只有当窗口向右滑动时才能发送更多字节

窗口何时滑动:

  • 当ACK号增加时(左边界的字节被确认)
  • 窗口整体向右移动

示例:

`初始: [100-199] 在传输中,窗口大小=100 左边界=100, 右边界=199

收到ACK=150: 窗口滑动: [150-249] 左边界=150, 右边界=249 现在可以发送字节200-249`

8.5 窗口大小的决定因素

流量控制:

  • 窗口大小 = 接收方通告的窗口
  • 接收方根据接收缓冲区可用空间决定通告窗口

拥塞控制:

  • 窗口大小还受拥塞控制算法限制
  • 两者取较小值

八、检测丢失和重传数据

9.1 两个触发条件

只需满足一个(不是两个都要)就触发重传:

9.2 触发条件1:超时(Timer)

Packet视角(之前):

  • 每个packet有一个计时器
  • 计时器到期且未被ACK → 重传该packet

字节视角(现在):

  • 只有一个计时器,对应第一个未确认字节(窗口左边界)
  • 计时器到期 → 重传最左边的未确认segment

计时器机制:

  • 计时器长度基于RTT
  • RTT通过测量"发送数据→收到ACK"的时间估算
  • 每次收到新ACK(窗口改变)时,重置计时器

9.3 触发条件2:重复ACK

Packet视角(之前):

  • 使用累积ACK
  • 收到K个重复ACK(通常K=3)时重传packet
  • 重复ACK表示后续packet被确认了

字节视角(现在):

  • 收到K个重复ACK时
  • 重传最左边的未确认segment

原理:

  • 重复ACK意味着:
    • 接收方收到了后续的数据
    • 但某个早期数据仍然缺失
    • 该数据很可能已丢失

九、TCP Header结构

10.1 端口号(各16位)

  • 源端口(Source Port):16位
  • 目的端口(Destination Port):16位

10.2 序列号和确认号(各32位)

  • 序列号(Sequence Number):32位
    • 该packet中第一个字节的字节偏移量
  • 确认号(Acknowledgement Number):32位
    • 已收到的最高连续序列号+1

10.3 校验和(Checksum)

  • 覆盖整个数据(不仅是header)
  • 用于检测数据损坏

10.4 通告窗口(Advertised Window)

  • 用于支持流量控制和拥塞控制
  • 接收方告诉发送方自己的接收窗口大小

10.5 Header长度(Header Length)

  • 指定TCP header中**4字节字(word)**的数量
  • 假设没有额外选项,长度=5(5×4=20字节)

10.6 标志位(Flags)

一系列可以设置为0或1的位:

SYN(Synchronize)标志:

  • 当主机发送ISN时开启
  • 通常只在握手的前两个消息中启用

ACK(Acknowledge)标志:

  • 当确认号有效且被使用时开启
  • 如果要发送数据但没有需要ACK的数据:
    • 可以关闭此标志
    • 告诉对方忽略ACK号字段

FIN(Finish)标志:

  • 请求关闭连接

RST(Reset)标志:

  • 强制重置连接

10.7 保留位(Reserved Bits)

  • Header长度后有6个保留位
  • 总是设置为0
  • 可以忽略

10.8 紧急指针(Urgent Pointer)

  • 可以标记某些字节为紧急
  • 告诉接收方尽快将这些数据发送给应用层
  • 历史遗留字段,现代较少使用

10.9 选项(Options)

  • TCP header末尾可以附加额外选项(使header变长)
  • 示例:选择性确认(SACK)
    • 如果想实现完全信息ACK
    • 可以添加SACK选项到header中

Header结构可视化:

0 16 31 +-------------------+-------------------+ | Source Port | Destination Port | +-------------------+-------------------+ | Sequence Number | +---------------------------------------+ | Acknowledgement Number | +-------+-------+-----------------------+ |Header | Rsv | Flags | |Length | | |U|A|P|R|S|F| | +-------+-------+-----------------------+ | Window | Checksum | +---------------------------------------+ | Urgent Pointer| Options... | +---------------------------------------+

拥塞控制问题

由于路由器的发送能力有限, 如果多个数据包同时到达路由器, 数据包将在队列里等待按顺序发送. 如果数据包的数量多, 发送将有很大延迟, 此为拥塞.

如何避免拥塞?

从资源分配的角度来看,我们希望一个好的拥塞控制算法能够实现三个目标。

我们希望资源分配高效。链路不应过载,数据包延迟和丢包率应降至最低。此外,链路应尽可能得到充分利用。

我们也希望连接间的资源分配公平。公平的定义我们稍后会正式确定,但简单来说,每个连接都应该共享相等的可用容量。

我们希望找到一个能够平衡这些目标的方案。虽然可以牺牲其他目标来优化其中一个目标,但这会导致糟糕的解决方案。例如,我们可以通过让所有节点以极快的速度发送数据包来确保链路利用率最大化(糟糕的方案,会导致网络拥塞)。或者,我们也可以通过让所有节点以极慢的速度发送数据包来确保丢包率最小化(糟糕的方案,无法充分利用网络容量)。

从更实际的系统角度来看,我们提出的解决方案需要具备可扩展性和去中心化特性。此外,我们的解决方案还应该能够适应网络变化(例如拓扑结构变化、连接的创建和销毁)。

基于主机的拥塞控制

TCP根据丢包数量,或延迟时间. 来判断是否发生拥塞.

设定初始速率, 判断是否阻塞, 如果否,则增加速率, 若是减少速率.直到趋于稳定.

问题是初始速率怎么设定? 增加减少速率的量怎么设定?

初始速率通常是不会产生拥塞的较少值.

增加速率是加一个固定的值. 减少速率是除一定的数.

相比于其他方式, 这个方式更能收敛到更公平,且利用率更高的速率.

总结:首先通过慢启动(从低速开始,指数级增长)探索初始速率。随后在每个迭代周期中,若检测到拥塞(通过丢包判断),则对R值进行倍数缩减;若未检测到拥塞,则对R值进行增量提升。

以下是拥塞控制解决方案的演进和分类总结:


1. 基于“端点”的拥塞控制 (Host-Based)

这是最传统、部署最广泛的策略。它遵循“智能端点,哑网络”的设计哲学。

  • 核心思想: 路由器(网络核心)只负责转发数据包(先进先出 FIFO),不参与拥塞控制。发送方(主机)必须自己推断网络是否拥塞,并主动调整其发送速率。
  • 代表:TCP (Tahoe, Reno, New Reno)
  • 如何工作:
    • 信号: TCP 将丢包(Packet Loss)作为网络拥塞的唯一信号。
    • 算法 (AIMD):
      1. 慢启动 (Slow Start): 启动时,速率呈指数增长,以快速探测可用带宽。
      2. 拥塞避免 (AIMD): 当速率超过 $SSTHRESH$(慢启动阈值)后,进入“加法增大” (AI) 阶段——缓慢地、线性地(每 RTT 增加 1 $CWND$)增加窗口,继续试探。
      3. 拥塞处理 (MD):
        • 超时 (Timeout): 视为严重拥塞。$CWND$ 降为 1, $SSTHRESH$ 降为当前 $CWND$ 的一半,返回“慢启动”。
        • 3 个重复 ACK (Fast Retransmit): 视为轻微拥塞。$SSTHRESH$ 降为当前 $CWND$ 的一半,$CWND$ 也降为一半(而不是 1),进入“快速恢复”(Fast Recovery),然后转入“拥塞避免”。
  • 优点:
    • 易于部署: 这是其最大的优势。它只需要在发送方和接收方的操作系统中实现,不需要对网络中的路由器进行任何更改。这就是它在 80 年代能被快速部署并拯救互联网的原因。
  • 缺点 (正如你总结的):
    • 信号模糊: 无法区分“拥塞丢包”和“损坏丢包”。
    • 延迟敏感 (Bufferbloat): 它必须填满路由器的队列(缓冲区)并导致丢包才能检测到拥塞,这会带来非常高的排队延迟。
    • 短连接效率低: 大多数短连接在“慢启动”阶段就结束了,从未达到过公平的速率。
    • RTT 不公平: RTT 短的连接能更快地增加 $CWND$,从而抢占更多带宽。

2. 基于“网络”的拥塞控制 (Router-Assisted)

这种策略认识到“端点”是盲目的,而路由器(拥塞的发生地)拥有最准确的信息。它遵循“智能网络”的哲学。

  • 核心思想: 路由器主动参与拥塞管理,要么强制执行公平,要么明确通知拥塞。
  • 代表 1:公平队列 (Fair Queuing, FQ)
    • 策略: 路由器管理拥塞。
    • 如何工作: 路由器不再使用单一的 FIFO 队列,而是为每个数据流(例如,每个 TCP 连接)维护一个单独的队列。当链路有空闲时,它会以“轮询”或其他公平算法(如最大最小公平)从这些队列中提取数据包。
    • 优点: 提供了隔离。一个“作弊”或高带宽的流无法通过填满队列来“饿死”其他流。它在单个链路上完美地解决了公平性问题(包括 RTT 和“作弊”问题)。
    • 缺点: 极其复杂。路由器需要以极高的速度(线速)解析每个数据包、维护成千上万个队列的状态并执行复杂的调度算法。这在实践中成本高昂。
  • 代表 2:显式拥塞通知 (Explicit Congestion Notification, ECN)
    • 策略: 路由器通知拥塞。
    • 如何工作: 当路由器感觉到队列开始增长(丢包发生之前),它不会丢弃数据包,而是在数据包的 IP 头中设置一个“ECN”标志位。接收方看到这个标志后,会在 ACK 中通知发送方。
    • 优点: 完美解决了“信号模糊”问题。发送方现在收到了一个清晰的信号:“网络正忙,请减速”,而不是“我丢了一个包,原因不明”。这允许 TCP 在不丢包的情况下降低速率,从而显著降低延迟。
    • 缺点: 需要路由器和端点双方都支持。虽然 ECN 已经标准化,但在全球互联网上的部署仍然不完整(但在数据中心等受控环境中非常有效)。

3. 基于“模型”的拥塞控制 (Model-Based)

这是最新的演进方向,它试图从根本上改变“信号”的来源。

  • 核心思想: 不再依赖“丢包”(一个滞后指标),也不完全依赖路由器。发送方自己构建一个关于网络路径的数学模型,并试图找到该模型的“最优运行点”。
  • 代表:Google BBR (Bottleneck Bandwidth and RTT)
  • 如何工作:
    • 信号: BBR 不再关心丢包(它甚至会在有轻微丢包时继续加速),而是持续测量两个关键指标:
      1. $BtlBw$ (瓶颈带宽): 路径的“最窄”部分的容量。
      2. $RTprop$ (最小 RTT): 路径的“物理”延迟(当队列为空时)。
    • 算法: BBR 试图将自己的发送速率精确地匹配到 $BtlBw$,同时只发送 $BtlBw \times RTprop$(即“带宽延迟积”)的数据量,以保持队列始终接近于
  • 优点:
    • 高吞吐、低延迟: 它的目标是同时实现高带宽利用率和极低的排队延迟,直接解决了 TCP 的 Bufferbloat 问题。
    • 对丢包不敏感: 在无线网络等高“损坏丢包”率的环境中,BBR 的性能远超 TCP,因为它不把这种丢包视为拥塞。
  • 缺点:
    • 公平性问题: BBR 是一种全新的范式,它与传统的基于丢包的 TCP(如 Reno/Cubic)共存时,可能会表现出“侵略性”,抢占过多带宽。
    • 复杂性: 算法本身非常复杂,仍在不断演进。

总结对比

| 类别 | 核心策略 | 拥塞信号 | 优点 | 缺点 | | --- | --- | --- | --- | --- | | 1. 端点 (TCP Reno/Cubic) | 推断 (Infer) | 丢包 (Loss) | 部署简单,久经考验 | 延迟高 (Bufferbloat),信号模糊 | | 2. 网络 (FQ / ECN) | 管理 / 通知 | 队列状态 / ECN 标志 | 公平性好,延迟低 | 依赖路由器,部署困难 | | 3. 模型 (Google BBR) | 建模 (Model) | 带宽 / RTT 测量 | 高吞吐、低延迟 | 算法复杂,公平性存疑 |

最终,拥塞控制是一个仍在活跃研究的领域,现代的解决方案(如 BBR)和网络辅助方案(如 ECN)正试图解决传统 TCP 遗留下的根本性问题。

UDP

UDP为应用层提供的功能只有port和checksum.不保证可靠性和拥塞控制

DNS

这是一个关于DNS (Domain Name System) 的详细技术总结。DNS是互联网的基础设施之一,其核心功能是将人类可读的域名 (如 google.com) 翻译成计算机可读的IP地址 (如 172.217.4.174)

以下是所提供内容的详细概括:

1. DNS 的核心问题与历史

  • 问题: 人类不善于记忆IP地址,而计算机需要IP地址来进行路由。
  • 早期解决方案: 一个名为 hosts.txt 的中央文件,由一个人(Elizabeth Feinler)手动维护并分发。
  • 问题: 这种方法完全不可扩展,更新缓慢(最初是纸质分发),并且存在单点故障。
  • DNS 的诞生: 1983年提出,旨在解决 hosts.txt 的问题。其核心设计目标是:
    • 可扩展性 (Scalability): 能够处理全球互联网的庞大规模和频繁变动。
    • 高可用性 (High-availability): 没有单点故障。
    • 高性能 (Fast/Lightweight): 几乎每个网络连接都始于DNS查询,因此它必须快。

2. DNS 的核心结构:分层与区域

DNS通过一个分布式、分层的树状结构来取代单个中央服务器。

  • Name Servers (域名服务器): 专用于响应DNS请求的服务器。没有一台服务器知道所有答案,而是将查询任务分散开。
  • Zone (区域): 每个域名服务器只负责一个特定的“区域”(Zone),例如 .com 区域或 berkeley.edu 区域。
  • 三位一体的层级:
    1. 命名层级 (Naming): 域名的结构,如 eecs.berkeley.edu,从右到左层级逐渐具体。
    2. 基础设施层级 (Infrastructure): 服务器的组织结构。
      • Root Servers (根服务器): 位于树的顶端。
      • Top-Level Domain (TLD) Servers (顶级域服务器): 负责 .com, .edu, .org 等。
      • Authoritative Servers (权威服务器): 负责特定域名(如 berkeley.edu)的服务器。
    3. 管理层级 (Authority):
      • ICANN 管理根区域,并将TLD授权给特定组织。
      • 这些组织(如管理 .edu 的)再将二级域名(如 berkeley.edu)的权威(authority)委托给其他人(如加州大学伯克利分校)。

3. DNS 查询的两种方式

A. 概念上的“迭代查询” (Iterative Query)

这是DNS的底层工作原理,由客户端(或解析器)主导。

  1. 客户端向根服务器(Root Server)发送请求:" eecs.berkeley.edu 的IP是什么?"
  2. 根服务器回复:"我不知道,但 .edu 区域的服务器(如 a.edu-servers.net)知道。这是它的IP地址。"
  3. 客户端向 .edu 服务器发送请求。
  4. .edu 服务器回复:"我不知道,但 berkeley.edu 区域的服务器(如 adns1.berkeley.edu)知道。这是它的IP地址。"
  5. 客户端向 berkeley.edu 服务器发送请求。
  6. berkeley.edu 服务器回复:"我知道!eecs.berkeley.edu 的IP地址是 23.185.0.1。"

B. 实践中的“递归查询” (Recursive Query)

这是如今大多数用户设备实际执行的方式。

  1. 你的电脑(称为 Stub Resolver)向一个递归解析器 (Recursive Resolver) (如Google的 8.8.8.8、Cloudflare的 1.1.1.1 或你的ISP的解析器)发送一个递归请求。
  2. 这个递归解析器代替你执行上述所有1-6步的迭代查询
  3. 解析器获取最终答案(23.185.0.1)后,将其返回给你的电脑。
  4. 缓存 (Caching) 至关重要:递归解析器会缓存这个答案。如果其他人很快也查询 eecs.berkeley.edu,解析器可以直接返回缓存的答案,无需再次查询根服务器,速度极快。缓存时间由记录的 TTL (Time-To-Live) 决定。

4. DNS 的技术实现细节

协议:UDP 优先

  • DNS 通常使用 UDP 而不是TCP,运行在端口 53上。
  • 为什么用 UDP?
    • 快: 无需TCP的三次握手。
    • 轻量级: 服务器无需为每个连接维护状态,可以独立处理每个数据包。
  • 如何处理不可靠? 如果客户端在超时时间内未收到响应,它会简单地重试
  • TCP 的例外: 当响应数据非常大时(例如在主、辅服务器之间进行区域传输 (Zone Transfer))或在某些安全扩展(如DNSSEC)中,会使用TCP。

DNS 报文格式

DNS查询和响应共享相同的格式,主要包含一个头部和四个数据区:

  • Header (头部):
    • Identification (16位ID): 一个随机数,用于将响应与原始查询匹配起来。
    • Flags (标志位): 标记这是查询(QR=0)还是响应(QR=1),是否为递归查询(RD=1),以及查询是否成功(NOERROR)或域名不存在(NXDOMAIN)。
  • Four Sections (四个区域):
    1. Question (问题区): 包含正在查询的域名和记录类型(如 "A" 记录)。
    2. Answer (答案区): 包含对查询的直接答案(例如,eecs.berkeley.edu 的A记录)。
    3. Authority (权威区): 包含 NS 记录,指示应该去哪个下一级权威服务器查询。
    4. Additional (附加区): 包含 Authority 区中NS记录对应的 A 记录(即IP地址)。这是一个关键优化,避免了客户端需要再次查询来获取下一级服务器的IP。

关键的资源记录 (Resource Records, RRs)

  • A Record: Name -> IPv4 地址。
  • AAAA Record: Name -> IPv6 地址。
  • NS Record: Zone -> Name Server 的域名。(用于委托)
  • CNAME Record: Name -> Name。用于创建别名(例如 www.example.com 指向 example.com)。

5. DNS 的高级应用与可用性

A. 根服务器可用性:Anycast (任播)

  • 表面上只有13个根服务器的IP地址,但这具有误导性。
  • 现实中,有成百上千台物理根服务器分布在全球。它们使用一种叫做 Anycast 的技术,共享这13个IP地址。
  • 当你向一个根IP发送数据包时,互联网的路由协议会自动将其发送到离你最近(网络上)的那台物理服务器。
  • 这提供了极高的冗余性低延迟

C. 负载均衡

  • DNS可以为一个域名返回多个 A 记录。客户端(如浏览器)通常会随机选择一个使用,从而将流量分配到不同的服务器上。
  • 地理负载均衡: 权威DNS服务器可以检测查询的来源(即解析器的IP地址),并返回地理上最接近用户的服务器IP地址,以减少延迟。
  • PTR 记录: 用于反向DNS查询,即通过IP地址查找其对应的域名。

HTTP

好的,这是您提供的关于 HTTP 协议的详细内容的总结。

📜 HTTP 基础与历史。。

  • 核心特性:
    • 基于 TCP: HTTP 运行在可靠的 TCP 协议之上,无需担心数据包丢失或乱序。
    • 客户端-服务器模型: 客户端(如浏览器)发起请求,服务器(如网站)进行响应。
    • 请求-响应模型: 每一个请求都对应一个响应。
    • 端口: HTTP 默认使用 80 端口,HTTPS(安全版)使用 443 端口。

📩 HTTP 报文结构

HTTP 报文(请求和响应)是人类可读的纯文本。

1. HTTP 请求 (Request)

包含三个主要部分:

  • 方法 (Method): 客户端希望执行的操作。
    • GET 获取资源(例如网页、图片)。请求内容通常为空。
    • POST 向服务器提交数据(例如表单)。数据包含在请求内容中。
    • 其他方法:HEAD (仅获取头部), PUT, DELETE 等,用于更复杂的操作。
  • URL: 标识要操作的资源路径。
  • 版本:HTTP/1.1
  • 头部 (Headers) (可选): 提供请求的元数据(附加信息)。
  • 内容 (Content) (可选):POST 请求发送的实际数据。

2. HTTP 响应 (Response)

包含四个主要部分:

  • 版本:HTTP/1.1
  • 状态码 (Status Code): 告知客户端请求的结果。
    • 2xx (成功):200 OK (请求成功), 201 Created (资源创建成功)。
    • 3xx (重定向):301 Moved Permanently (永久移动), 302 Found (临时移动)。
    • 4xx (客户端错误):404 Not Found (未找到), 401 Unauthorized (未授权), 403 Forbidden (禁止访问)。
    • 5xx (服务器错误):500 Internal Server Error (内部错误), 503 Service Unavailable (服务不可用)。
  • 可选消息: 对状态码的人类可读描述(如 "OK", "Not Found")。
  • 内容 (Content): 实际返回的数据(如 HTML 页面、图片数据等)。

3. HTTP 头部 (Headers)

头部是 HTTP 协议可扩展性的关键。它们是键值对形式的元数据,用于在请求和响应中传递附加信息。

  • Content-Type 说明内容的媒体类型(如 text/html, image/jpeg),告诉浏览器如何解析数据。
  • Host 指明服务器上托管的特定网站(因为一台服务器可能托管多个网站)。
  • User-Agent 客户端的浏览器或程序信息。
  • Location 在 3xx 重定向响应中,指明资源的新位置。
  • Cache-Control 用于实现缓存策略(见下文)。

🚀 HTTP 性能优化

为了加快网页加载速度,HTTP 采用了多种优化策略,其中最关键的是缓存

1. 流水线 (Pipelining)

  • HTTP/1.1 引入了流水线技术,允许在同一个 TCP 连接上连续发送多个 HTTP 请求并接收响应,避免了为每个请求都重新进行 TCP 握手的开销。

2. 缓存 (Caching)

缓存是避免重复请求相同数据的核心策略,它对客户端、ISP 和服务器三方都有利(减少延迟、降低带宽占用、减轻服务器压力)。

  • 缓存类型:
    • 私有缓存 (Private): 客户端本地缓存(如浏览器缓存),仅供单个用户使用。
    • 代理缓存 (Proxy): 位于网络中(如 ISP 控制),可被多个用户共享。
    • 托管缓存 (Managed): 由应用提供商(如 Google)控制和部署的网络缓存。
  • 静态 vs. 动态内容:
    • 静态内容(如 Logo 图片、视频文件)可以被积极缓存。
    • 动态内容(如搜索结果、个性化页面)通常不缓存或使用私有缓存。
  • 实现方式 (通过 Headers):
    • Expires (HTTP/1.0): 指定一个绝对的过期时间。
    • Cache-Control (HTTP/1.1): 更灵活的缓存控制。
      • private: 只能私有缓存。
      • max-age=86400: 缓存 1 天。
      • no-store: 完全禁止缓存。

🌍 内容分发网络 (CDN)

CDN 是托管缓存 (Managed Caches) 的大规模应用。

  • 概念: CDN 是一个由应用提供商(或 CDN 服务商如 Cloudflare)在全球部署的、靠近用户的服务器网络,用于存储和分发内容(尤其是静态内容)。
  • 优势:
    • 低延迟: 用户从“就近”的服务器获取数据,速度更快。
    • 高可扩展性: 通过增加更多缓存服务器来扩展服务,而不是依赖单一的源服务器。
    • 高可用性 (冗余): 单个 CDN 节点故障,请求可以自动转向其他节点。
    • 节省带宽: 大量流量被 CDN 承载,减轻了源服务器和骨干网的压力。
  • 部署: CDN 节点可以部署在应用提供商的网络边缘,甚至深入部署到 ISP 的网络内部(这对 ISP 和应用商是双赢的)。
  • 客户端导向: 如何将用户导向最近的 CDN 节点?
    • Anycast (任播): 多个服务器使用同一个 IP 地址,路由协议会自动找到“最近”的。
    • DNS 负载均衡: DNS 服务器根据客户端 IP 返回一个“最近”的服务器 IP 地址。
    • 应用层映射 (推荐): 源服务器在 HTML 响应中,直接指定 CDN 资源的 URL(如 static.google.com),或通过 3xx 重定向。

💡 HTTP 的演进

  • HTTPS (HTTP Secure):
    • 在 HTTP 和 TCP 之间增加了 TLS (传输层安全) 协议。
    • 通过 TLS 握手交换密钥,对所有 HTTP 报文进行加密,防止窃听和篡改。
  • HTTP/2.0 (2015年):
    • 目标: 解决 HTTP/1.1 的性能瓶颈,降低延迟。
    • 新特性: 头部压缩、请求优先级、服务器推送(Server Push,服务器可主动推送资源)、更高效的多路复用。
  • HTTP/3.0 (2022年):
    • 最大变革: 不再基于 TCP 运行,而是基于 QUIC 协议。
    • QUIC (快速UDP网络连接): 这是一个基于 UDP 的新传输协议,旨在进一步减少连接和传输延迟(例如解决了 TCP 的队头阻塞问题)。
    • 意义: 为了追求极致性能,HTTP/3.0 打破了严格的网络分层模型,将传输层(QUIC)和应用层(HTTP/3.0)紧密结合进行设计。

HTTPS

TLS握手: client与server再建立TCP连接之后, client会向server发送client hello, 包含一个随机数. 服务器向client 发送 server hello, 包含一个随机数, 以及网站证书. 这个证书有权威机构用自己的私钥签名.(防止其他网站伪造). 用户拿到证书用自己电脑本地的公钥对其证书真实性验证. 验证通过后,确认网站的身份. 生成预主密钥, 再用证书里的公钥加密, 发送给服务器. 服务器用自己的私钥解密, 获得预主密钥. 双方再利用预主密钥以及两个随机数生成最终的会话密钥.双方互相发送finished信息. 然后信息都由该密钥加密,以及解密.