简单回顾attention流程

1
2
3
4
5
6
7
8
9
10
11
12
13
【输入向量 X】(来自上一层或Embedding)

├─── 步骤①:解耦分裂 ───> 动态乘以 Wq, Wk, Wv 权重,生成 Q, K, V 矩阵
│ (此时 V 矩阵诞生了,和 FFN 还毫无关系)

├─── 步骤②:注意力对齐 ─> 计算 Q 乘以 K 的转置 (Q·K^T),得到亲疏得分矩阵

├─── 步骤③:归一化 ─────> 经过 Softmax 激活,把得分变成加起来等于 1 的权重

├─── 步骤④:财富提取 ─────> 用 Softmax 的权重去乘以 V 矩阵 (Softmax·V)


【Attention 层的最终输出】 ───> 这时,一次完整的 Attention 计算宣告结束!

qkv矩阵的含义是怎样的

我觉得可以从他们本身的名字入手

1. Q (Query - 查询)

  • 含义:当前词“试图去关注(或询问)其他词”的向量表示。
  • 角色:它是主动方。在 Self-Attention 中,每个词都会轮流作为 $Q$,去询问句子里的所有词(包括它自己):“你们谁和我有关系?关系有多大?”

2. K (Key - 键)

  • 含义:当前词“被其他词关注时的特征(或身份标签)”的向量表示。
  • 角色:它是被动方。它存在的意义就是和 $Q$ 做点积(Dot Product),来计算两个词之间的相关性或相似度。

3. V (Value - 值)

  • 含义:当前词“本身所包含的、最核心的内容信息”。
  • 角色:一旦 $Q$ 和 $K$ 算出了注意力权重(即谁和谁更相关),这些权重就会乘到对应的 $V$ 上。最终把这些加权后的 $V$ 累加起来,就得到了该词融合了上下文信息后的新表示。

我的理解:

q可以理解成新词关注历史字符的向量,也就是query,k则是历史字符被关注时的向量,两者相乘可以获得关联度

v可以代表每一个词在进入这一层时,通过与权重矩阵相乘,提取出了它最核心的、未经污染的语义特征(比如“苹果”包含了:水果、甜的、红的、科技公司、iPhone 等属性)。这个 V 就是这个词的“属性本体”。

kvcache是什么

在多次对话的过程中,前面的历史字完全没变,它们算出来的 $K$(身份标签)和 $V$(词本身的属性)也就是固定不变的。那算过一次之后,直接把它们存在内存(显存)里,后面就可以直接拿来用,因此降低了计算量,提高了响应的速度

那为什么不存q矩阵呢

这个问题的关键还是要理解qkv矩阵的含义和kvcache的流程

q矩阵代表着最新字符对于历史字符关注的向量,而新字符是不对吐出来实时变化的,所以存储q矩阵没有意义

而这里要区分一下大模型生成的两个阶段:

阶段一:Prefill(预填充阶段 —— 读懂你的 Prompt)

当你把一句话(比如“请问什么是Attention”)发给模型时,模型会一次性把这几个字全部输入进去。

  • 这个时候,所有词都要互相看。所以每个词的 $Q、K、V$ 全都要计算,以此来捕捉整个句子的上下文语义。
  • 计算完后,模型会把这些历史词的 $K$ 和 $V$ 老老实实地存进内存里。这就是 KV Cache 的初始化

阶段二:Decoding(解码阶段 —— 逐字生成)

这就是你刚才描述的绝妙场景。假设模型现在吐出了一个新词:“它”

  • $Q$ 是实时的、一次性的

    “它”这个新词作为当前的唯一主角,会产生一个崭新的 $Q{它}$。它需要拿着这个 $Q{它}$ 去跟前面所有的历史词进行比对。

    💡 为什么不用缓存 $Q$? 因为当“它”这个词处理完、吐出下一个词(比如“是”)之后,主导权就交给了“是”的 $Q$。旧的 $Q_{它}$ 在未来的生成中永远都不会再被用到了。它是一次性的消耗品。

  • $K$ 和 $V$ 必须死死存住

    为了算出“它”和历史词的关联度,你需要历史词的 $K$;为了融合出“它”的新向量,你需要历史词的 $V$。

    • 如果我们不缓存 $K$ 和 $V$,每生成一个新词,模型就得把前面所有的历史词重新做一遍矩阵乘法,去重新算一遍它们的 $K$ 和 $V$。
    • 随着生成的句子越来越长,这种重复计算会呈爆炸式增长(计算量 $O(N^2)$),模型会越写越卡。
    • 有了 KV Cache:历史词的 $K$ 和 $V$ 已经在内存里躺着了。新词“它”算完自己的 $Q、K、V$ 后,拿自己的 $Q$ 去和内存里的一大推历史 $K$ 做乘法,算完后,再顺手把自己的 $K{它}$ 和 $V{它}$ 也追加到缓存的末尾,供下一个词使用。

SSE(Server-send events)

SSE 是一种单向通信技术,允许服务器主动向客户端(通常是浏览器)推送实时数据。

在过去,网页想要获取服务器的实时数据,要么用短轮询(每隔几秒发一次 HTTP 请求,低效且浪费资源),要么用 WebSocket(双向奔赴,但协议较重,开发成本高)。而 SSE 则是介于两者之间的一个轻量级极佳方案。

核心工作原理

image-20260527185951958

1. 建立长连接(Keep-Alive)

普通的 HTTP 请求是“一问一答”,服务器说完话连接就立刻断开。而 SSE 在客户端发起请求后,服务器会返回特殊的响应头,告诉浏览器:“这个连接别挂断,后面还有货!”

这组特殊的响应头通常是:

  • Content-Type: text/event-stream:告诉浏览器,这是一个流式的数据流。
  • Cache-Control: no-cache:禁止缓存,确保数据的实时性。
  • Connection: keep-alive:保持长连接。

2. 文本流数据格式

连接建立后,服务器就可以通过这个通道持续发送文本数据。SSE 对发送的数据格式有严格的要求,它是由多条消息组成的文本流,每条消息内部用换行符 \n 分隔,消息之间用两个换行符 \n\n 分隔。

最基本的结构如下:

1
data: 这是第一条消息\n\n

id: 1\n event: chat\n data: 这是第二条带事件类型和ID的消息\n\n

3. 自带“断线重连”基因

在网络不稳定的现实世界中,连接断开是常有的事。SSE 的高级之处在于,浏览器的原生 API(EventSource)内置了自动重连机制

  • 如果网络断开,浏览器会自动尝试重新连接服务器(默认每隔 3 秒)。
  • 服务器在发送数据时可以附带一个 id 字段。断线重连时,浏览器会自动在 HTTP 请求头中带上 Last-Event-ID: [上次收到的ID]。服务器看到后就能心领神会,把客户端错过的消息补发给它。

Claude 的 Messages API 与 OpenAI 的 Responses API

前者是无状态(Stateless),每次对话会将历史记录传回服务端,利用promptcaching技术(大模型服务器의网关会计算你这段历史文本的哈希值(Hash),当发现前面的字符和之前一致时,会从内存的 KV Cache读取,而不是重新计算)。

前者第二个特点是结构上的,核心差别就是claude选择使用数组存储,称之为 (Content Blocks),里面按时间顺序并列存放不同的“块”(如思维链块、文本块、工具调用块)。这里可以和传统的openaiCompletions api进行对比思考,传统的Completions是将对话存入一个”message”对象的字段下,”message”对象内包含”content””reasoning_content””role”等字段。

但核心问题就是,JSON 语法绝对不允许出现重复的键(Key),如果单次对话出现多次工具调用或思考,Completions很难正确表示这个过程。

我的分析是,旧的Completions还停留在与模型对话一问一答的形式,而现在agent通常一次对话包含多次工具调用或模型思考,因此Completions不再适用,从而claude选择使用数组存储多个json对象,每个json使用type区分块的功能

后者结构上沿用和Claude相同的设计,使用json数组实现对对话内容的存储,每次请求和响应只会包含最新的一轮对话,而不像claude完整的历史记录,每个Response都会包含一个id,从而支持随时回滚;而item的id可以类比claude的type,说明单次的功能。

Plaintext

1
2
3
4
5
6
7
[第 1 轮对话]
Response (最顶层对象) ──>【你的本地数据库存它!】id: "**resp_A1B2C3...**"

└── output (内部数组)
├── Item 1 (思考块) ──> id: "**item_thinking_001**"
├── Item 2 (工具调用) ──> id: "**item_tool_002**"
└── Item 3 (最终回复) ──> id: "**item_text_003**"

后者最大的特点是在服务端是有状态(Stateful)工具调用和上下文历史管理完全托管在云端服务器

这样的好处有:

  1. 发新请求时,不用反复传完整的聊天记录,只要带上上一次回复的 previous_response_id,服务端会自动在后台回溯、拼接历史,甚至自动触发上下文压缩(Compaction)
  2. 云厂商将网络搜索、代码解释器等工具直接托管在云端。在单次 API 请求的生命周期内,模型如果发现需要联网或跑代码,会在云端沙盒里自己反复调用并纠错,跑完整个智能体循环后,直接把最终成果包吐给客户端,客户端只负责“解析最终数据”。
  3. 模型方面,openai可以因此保护模型的隐藏推理链(Reasoning Traces),OpenAI 就可以把所有的推理中间态完美地封锁在自己的云端服务器里,客户端只能拿到剥离后的干净结果。
  4. 数据方面,一旦完全托管在服务端,由于数据是由 OpenAI 的数据库统一规范存储的,服务器在计算 KV Cache 的持久化和重用时,可以做到对齐和命中

但这样也有坏处:

由于是云端调用工具,除了 Token 费,调用内置的联网搜索、代码沙箱通常需要额外支付固定通道费或容器会话费

image-20260527180345112

prompt caching

kvcache的原理不多赘述,简单来说,就是大模型会把kv矩阵计算的值存储起来,再次遇到历史字符时就不用计算了,只计算新字符的矩阵,最后拼接起来。

对于Completions API和Anthropic 官方的 Messages API,他们每次请求与详细都会传输完整的上下文,因此为了节省token的消耗,prompt caching便是关键

这里可以参考提示缓存 |OpenAI API

模型提示通常包含重复内容,如系统提示和通用指令。OpenAI 将 API 请求路由到最近处理过相同提示的服务器,这使得它比从零开始处理提示更便宜、更快。提示缓存可将延迟降低高达80%,输入令牌成本降低高达90%。提示缓存会自动处理你所有的 API 请求(无需修改代码),且没有额外费用。

缓存命中只能针对提示内的精确前缀匹配。

image-20260527185035144

工作流程

缓存功能对于1024个token及以上的提示会自动启用。当您发送API请求时,会执行以下步骤:

1.根据提示词初始前缀计算哈希值,去内存的 KV Cache 索引表里检索。

2.如果找到匹配的前缀,系统会使用缓存结果。

kv的存储

它最初在 GPU 显存(VRAM)里,但为了存更久,会被“吐”到 GPU 本地的 NVMe SSD 硬盘或内存中。

大模型的显存(VRAM)是极其昂贵且稀缺的资源。如果把所有用户的 KV 缓存都永久塞在 GPU 显存里,GPU 瞬间就会爆显存(OOM)。因此,厂商引入了分层存储架构:

  • 热缓存(In-Memory 状态):当模型刚处理完你的请求时,生成的 KV 张量确实老老实实呆在 GPU 显存(VRAM) 或该服务器节点的 系统内存(RAM) 中。因为读写速度最快,随时准备应对你几分钟内的下一轮对话。

  • 冷缓存(Extended 状态):当缓存需要保存几小时甚至 24 小时时,服务器会把这些 KV 张量进行加密,然后从珍贵的显存中释放出来,转储到 GPU 本地服务器的高速 NVMe SSD 硬盘 上。

    当你下一次请求命中缓存时,服务器会以极快的速度把加密的 KV 张量从本地硬盘重新加载(Load)回 GPU 显存中。虽然比纯显存慢一点点,但比起让大模型重新推理一遍几万字的输入,速度依然快了 80% 以上。

openai Completions API和Responses API区别

1. 响应数据结构(JSON)对比

两者的最直观区别在于返回的 JSON 报文层级和字段设计。

传统的 Completions API (以 Chat Completions 为例)

在传统规范中,模型生成的内容被深度嵌套在 choices 数组中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1716584520,
"model": "qwen-plus",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你好!请问有什么我可以帮你的?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 12,
"total_tokens": 21
}
}
  • 代码提取路径response.choices[0].message.content
  • 痛点:层级过深;由于早期的设计允许返回多个结果(通过 n 参数控制 choices 的数量),导致普通的单次对话也必须套一层数组。

全新的 Responses API

为了简化代码并原生支持更复杂的 AI 行为,新规范将结构打平,引入了 output 概念:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"id": "resp_123",
"object": "response",
"model": "qwen3.5-122b",
"output": [
{
"type": "message",
"reasoning": "用户询问两者的区别,我需要用表格和对比清晰展现...",
"message": {
"role": "assistant",
"content": "两者的主要区别在于..."
}
}
],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 45,
"total_tokens": 60
}
}
  • 代码提取路径:在新版 SDK 中,通常可以直接通过 response.output_text 拿到最终文本。
  • 亮点原生支持深度思考(Reasoning)。当使用具备类 o1/Qwen3.5 等带有思维链(CoT)功能的推理模型时,其思考过程会直接作为平级的 reasoning 字段输出,无需再像以前那样通过特殊的 content 拼接或不透明的魔改字段来传输。

2. 核心维度深度对比

维度 Completions API (传统) Responses API (新一代)
设计定位 纯文本生成 / 简单对话交互 彻底面向智能体 (Agentic Loop) 和新型推理模型设计
结构复杂度 较高。嵌套在 choices[0].message 较低。外层通常直接暴露 output 或扁平化字段
思维链支持 (Reasoning) 较弱。通常需要占用 content 空间或使用魔改字段透传 原生支持。拥有独立的 reasoning 字段,与最终回答完美分离
多步骤工具调用 (Tool Call) 复杂。多轮 Tool 调用需要开发者在代码里频繁手动拼装、维护上下文历史 原生自动化。支持单次请求内模型自主进行多步工具调用(如网络搜索、执行代码)并直接返回最终 Response
存储与状态保持 默认无状态(Stateless),每次交互需全量上传历史记录 部分环境支持原生上下文状态保持(Stateful),降低长对话的 Token 传输成本

Anthropic 官方的 Messages API 规范

样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"id": "msg_01A4zFr95E9A3daS7kL28xWq",
"type": "message",
"role": "assistant",
"model": "claude-3-7-sonnet-20250219",
"content": [
{
"type": "text",
"text": "没问题,我正在为您查询数据库中状态为活跃的前5个用户。"
},
{
"type": "tool_use",
"id": "toolu_01827391aBcDeFgHiJk",
"name": "query_database",
"input": {
"sql": "SELECT user_id, username FROM users WHERE status = 'active' LIMIT 5;"
}
}
],
"stop_reason": "tool_use",
"stop_sequence": null,
"usage": {
"input_tokens": 420,
"cache_creation_input_tokens": 350,
"cache_read_input_tokens": 0,
"output_tokens": 85
}
}

核心对比矩阵

维度 Claude 响应格式 (Messages API) OpenAI Responses API
设计核心 富内容块 (Rich Content Blocks) 驱动 智能体循环 (Agentic Loop) 与托管工具驱动
基础数据对象 消息 (messages) 与 内容块 (content) 项 (items) 与 响应对象 (response)
多轮状态管理 无状态 (Stateless):需由客户端每次回传完整上下文(配合 Prompt Caching 优化成本) 有状态 (Stateful):支持通过 previous_response_id 在服务端自动链式追踪状态
工具调用机制 客户端编排:模型返回 tool_use,开发者在本地执行后用 tool_result 回传 服务端托管:一次请求内,服务端自动运行多轮内置工具(Web 搜索、代码执行、MCP等)
结构化输出 支持 JSON Schema / 借助 SDK 的 messages.parse 确保强一致性 原生支持强类型的 Structured Outputs 和自动修复

核心差异深度解析

1. 返回数据结构:Content Blocks vs Output Items

  • Claude 的响应格式(块结构)

    Claude 的核心创新在于将响应内容拆解为一个个并列的 Content Block(内容块)。一条响应的 content 是一个数组,里面可以同时包含思维链(thinking)、文本、工具调用等多种类型。

    JSON

    1
    2
    3
    4
    5
    "content": [
    { "type": "thinking", "thinking": "用户需要计算..." },
    { "type": "text", "text": "好的,我为你算一下。" },
    { "type": "tool_use", "id": "toolu_123", "name": "calculator", "input": {...} }
    ]

    这种设计的优势在于透明度高、扩展性极强,完美融合了文本、多媒体(PDF/图片)和原生推理。

  • OpenAI Responses API(对象与项结构)

    Responses API 彻底抛弃了早期 Chat Completions 繁琐的 choices[0].message 嵌套,返回一个具有独立 idresponse 对象,其内部核心是 output 数组,由各种类型的 Item(项) 组成。

    1
    2
    3
    "output": [
    { "id": "item_abc", "type": "message", "message": { "role": "assistant", "content": [...] } }
    ]

    它在顶层解耦了 instructions(系统指令)和 input(用户输入),语义更加符合现代 Agent 架构。

2. 状态与多轮对话管理

  • Claude:客户端保留 + 提示词缓存 (Prompt Caching)

    Claude API 本身是无状态的。这意味着做多轮对话时,你必须把之前的聊天历史全部打包传回去。

    巧妙的优化: 尽管每次都要重传历史,但 Claude 提供了极其强大的 Prompt Caching(瞬时缓存)。只要历史对话没有变,重复传入的部分会直接命中缓存,不仅响应速度极快,还能帮你省下高达 90% 的输入 Token 费用。

  • OpenAI Responses API:服务端自动追踪 + 引用 ID

    Responses API 引入了真正的“有状态”模式。你不需要在客户端维护一个长长的数组,你只需要传入当前最新的用户输入,并附带上一次的 previous_response_id

    Python

    1
    2
    3
    4
    5
    response2 = client.responses.create(
    model="gpt-5",
    previous_response_id=response1.id,
    input="那它的常住人口是多少?"
    )

    OpenAI 的服务器会自动在后台保留和拼接历史,甚至在上下文过长时自动触发服务端压缩 (Compaction),免去了开发者手动管理长上下文的痛苦。

3. 工具调用与智能体编排 (Tool Use & Agentic Loop)

  • Claude:协作式编排

    当你给 Claude 绑定自定义工具时,交互是“回合制”的:客户端发请求 $\rightarrow$ Claude 返回 tool_use 块并中断请求 $\rightarrow$ 你的代码在本地执行该工具 $\rightarrow$ 你的代码把 tool_result 发回给 Claude $\rightarrow$ Claude 给出最终文本。这种模式下,控制权完全在开发者手里,你可以非常安全地在本地读取数据库或执行敏感操作。

  • OpenAI Responses API:全面托管的自动循环

    Responses API 的最大杀手锏是它的 Agentic by default(默认智能体化)。它把工具调用的控制权收归到了云端。

    它内建了诸如 Web Search(网络搜索)、File Search(文件搜索)、Code Interpreter(代码解释器)以及远程 MCP(模型上下文协议)服务器。当你发出一个请求时,模型可以在一次 API 调用的生命周期内,自己在云端反复调用多次工具,直到得出完美答案。开发者不需要写任何多轮编排的代码。

参考资料

Data controls in the OpenAI platform

Prompt caching | OpenAI API

简介

今天尝试使用hugging的transformer库跑通litert-community/DeepSeek-R1-Distill-Qwen-1.5B · Hugging Face

Qwen-1.5B 基底:它使用了阿里云开源的 Qwen2.5-1.5B 作为基础架构,参数量只有 15 亿(1.5 Billion),天生体积小、运行速度快。

DeepSeek-R1 蒸馏:DeepSeek 把他们那个 6710 亿参数、拥有强大推理思维能力的巨型模型(DeepSeek-R1)所产生的思维链(Chain of Thought)数据和推理样本,喂给了这个 1.5B 的小模型。

核心能力:这意味着它虽然只有 15 亿参数,却继承了 DeepSeek-R1 的“思考”习惯(会在 <think> 标签内进行自我纠错、逻辑推理),在数学、代码和逻辑推理上,性能远超普通的 1.5B 级别模型。

tranformer库和pytorch的区别

PyTorch(底层深度学习框架)

  • 角色:通用数学和张量(Tensor)计算库,专门为深度学习设计。
  • 干什么:它提供最基础的算子。比如图下半部分看到的 SoftmaxLinear(全连接层)、矩阵乘法、梯度反向传播,以及直接对接显卡硬件(NVIDIA CUDA、AMD ROCm、Apple Metal)的驱动支持。
  • 特点:它本身不知道什么是大语言模型,什么是 Llama 或 DeepSeek。它只知道如何高效地在 GPU 上做矩阵加减乘除。

Transformers 库(上层模型生态库)

  • 角色:由 Hugging Face 开发的、专为 Transformer 架构模型定制的高级封装库
  • 干什么:它把 PyTorch 提供的基础算子(Linear、Softmax 等)拼接起来,组装成一个个具体的经典模型结构(如 DeepSeek、Qwen、Llama)。
  • 特点:它让你不需要从零去写注意力机制(Attention)的数学公式,直接通过几行代码(如 AutoModelForCausalLM.from_pretrained('deepseek-ai/DeepSeek-R1'))就能把整个模型跑起来。

image-20260522175239547

下载模型

配置环境

1
2
uv add transformer huggingface_hub
uv add torch torchvision torchaudio --index https://download.pytorch.org/whl/cu128
1
2
3
4
5
6
7
8
9
10
11
12
13
import os

# 1. 必须最先设置环境变量(在 import huggingface_hub 之前)
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"

# 2. 然后再导入 Hugging Face 的下载工具
from huggingface_hub import snapshot_download

# 3. 最后执行下载
snapshot_download(
repo_id="deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B",
local_dir="models/DeepSeek-R1-Distill-Qwen-1.5B"
)

加载模型

先查看模型文件夹下的config.json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
{
// 模型的架构类名,告诉 Transformers 库在代码中应该用哪个具体的类来实例化这个模型
"architectures": [
"Qwen2ForCausalLM"
],

// 在注意力机制(Attention)层中 Dropout 的比例。0.0 代表不随机丢弃神经元节点
"attention_dropout": 0.0,

// 序列开始特殊标记(BOS, Begin of Sequence)的 ID 编码
"bos_token_id": 151643,

// 序列结束特殊标记(EOS, End of Sequence)的 ID 编码。模型吐出这个 ID 时就会停止生成
"eos_token_id": 151643,

// 隐藏层(前馈神经网络)中使用的激活函数类型。这里采用的是 SiLU(Sigmoid Linear Unit)
"hidden_act": "silu",

// 隐藏层的维度(模型的宽度)。即每个 Token 进入模型内部后转换成的向量长度(1536维)
"hidden_size": 1536,

// 权重参数初始化时的标准差。模型刚创建、未训练时,权重会在这个范围的随机正态分布中生成
"initializer_range": 0.02,

// 前馈神经网络(FFN/MLP)内部升维后的中间层维度。向量会在这里从 1536 临时扩大到 8960 维再缩回
"intermediate_size": 8960,

// 模型支持的最大位置编码长度。也就是该模型的理论最大上下文窗口(128K Context Window)
"max_position_embeddings": 131072,

// 启用滑动窗口注意力机制(Sliding Window Attention)的最大层数。这里前 21 层会受到相关策略影响
"max_window_layers": 21,

// 模型的类型基础名称,这里注册为千问2代架构(qwen2)
"model_type": "qwen2",

// 多头注意力机制(MHA)中 Query(查询)头的总数量
"num_attention_heads": 12,

// 模型的总层数(深度)。数据需要连续穿过 28 个 Transformer 块(Block)
"num_hidden_layers": 28,

// 分组查询注意力(GQA)中 Key 和 Value 头的数量。
// 12 个 Q 头共享 2 个 KV 头,能大幅减少推理时的 KV Cache 显存占用
"num_key_value_heads": 2,

// RMSNorm(均方根归一化)层为了防止分母为 0 而引入的极小常量值($\epsilon$)
"rms_norm_eps": 1e-06,

// 旋转位置编码(RoPE)的底数基频($\theta$)。控制位置编码在长文本下的外推和衰减敏感度
"rope_theta": 10000,

// 当启用滑动窗口注意力时,窗口的具体大小(限制只能看到过去 4096 个 Token)
"sliding_window": 4096,

// 是否绑定输入和输出的嵌入层权重。false 代表输入 Embedding 和输出 Linear 层的参数各自独立
"tie_word_embeddings": false,

// 模型训练和默认保存时的数据精度类型。bfloat16 代表 16位脑浮点数,兼顾范围与精度
"torch_dtype": "bfloat16",

// 导出该配置文件时,所使用的官方 Hugging Face Transformers 库的版本号
"transformers_version": "4.44.0",

// 是否默认开启键值缓存(KV Cache)。开启后可以避免重复计算历史 Token,实现流式快速生成
"use_cache": true,

// 是否启用多模态旋转位置编码(Multimodal RoPE),常用于处理视觉等多模态输入。这里为 false
"use_mrope": false,

// 是否在全局强制启用滑动窗口注意力机制。这里为 false,说明默认使用全量的全局注意力
"use_sliding_window": false,

// 词表的大小。代表模型一共认识 151,936 个不同的字、词或特殊符号
"vocab_size": 151936
}

在这个文件里:

  • "architectures": ["Qwen2ForCausalLM"]
  • "model_type": "qwen2"

说明这个模型底层架构为qwen2因果解码器

image-20260523110036536

1
2
3
4
5
6
7
from transformers import Qwen2ForCausalLM
model = Qwen2ForCausalLM.from_pretrained("models/DeepSeek-R1-Distill-Qwen-1.5B")
model

from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("models/DeepSeek-R1-Distill-Qwen-1.5B")
model

下面的AutoModelForCausalLM可以通过读取config文件,使用正确的架构加载模型

什么是ForCausalLM(因果语言模型)

ForCausalLM(因果语言模型)—— 只能看左边,猜右边

这种模型在训练和推理时,就像是在看一部正在播放的电影

通俗例子:

假设训练数据是一句话:“我 喜欢 吃 南京 盐水鸭”

当模型处理到 “吃” 这个字时:

  • 它能看到什么“我 喜欢”(左边的历史信息,它100%看得见)。
  • 它被隐藏了什么[南京 盐水鸭](右边的未来信息。模型被戴上了特殊的“向后看”的眼罩,技术上叫因果掩码,强行把右边的字遮住)。
  • 它的任务:根据 “我 喜欢 吃”,去猜后面接 “南京” 的概率是多少。

为什么必须隐藏后面的? 因为如果训练时不把右边的 “南京 盐水鸭” 挡住,模型就会直接去偷看标准答案,这就成了“作弊”。这样训练出来的模型在实际聊天时,由于没有未来可以偷看,就会彻底废掉。

ForMaskedLM(掩码语言模型)—— 整句话全看见,做完形填空

这就对应了你说的“一整句话都能看到”的模型。它在训练时不需要戴单向眼罩。

通俗例子:

同样是这句话:“我 喜欢 吃 南京 盐水鸭”

AI 老师在训练它时,直接把整句话拍在它面前,但是用黑笔随机涂黑(Mask)了一个词:

  • 输入给模型的“我 喜欢 吃 [ ❌ ] 盐水鸭”
  • 它能看到什么:它能同时看到左边的 “我 喜欢 吃” 和右边的 “盐水鸭”。整句话的上下文它尽收眼底。
  • 它的任务:像做高考完形填空一样,结合两边的线索,推测被涂黑的 [ ❌ ] 里面有高概率是 “南京”

qwen与deepseek架构的区别

image-20260523100747237

image-20260523100939383

区别一:前馈网络(FFN)的“大一统” vs “MoE 混合专家”

这是两家最本质的区别。

  • Qwen(Dense 稠密流派): Qwen2.5-72B 采用的是标准的 Dense 结构。这意味着无论是算 1+1= 这种简单的算术题,还是写复杂的 Linux 内核代码,模型在每一层里的前馈网络(FFN)都是全员上阵。720亿参数全部参与计算。
    • 优缺点:对硬件非常友好,算子成熟,但在参数量极大时,计算成本(算力消耗)呈线性飙升。
  • DeepSeek(MoE 专家流派): DeepSeek-V3 采用了独创的 DeepSeekMoE 架构。它把原本一个巨大的 FFN 拆分成了 1个共享专家(Shared Expert)256个路由专家(Routed Experts)
    • 独特设计:当一个 Token 传过来时,共享专家永远参与计算(抓取全局共性知识),而路由专家中只有最对口的 2 个会被激活(抓取垂直专业知识)。
    • 恐怖的性价比:DeepSeek-V3 的总参数量高达 6710亿(671B),但因为 MoE 的存在,每个 Token 进来时实际上只激活了 370亿(37B) 参数。这就是为什么它能用极低的算力成本,抗衡甚至超越其他几千亿规模的稠密模型。

区别二:注意力机制的“GQA” vs “MLA(低秩多头注意力)”

在处理上下文和长文本记忆(KV Cache)时,两家拿出了不同的看家本领。

  • Qwen(采用 GQA): 正如我们在它的 config.json 里看到的,Qwen 采用的是 GQA(Grouped-Query Attention)。它让多个 Query 头共用一组 KV 头。这在 2024~2026 年是行业标准设计,已经能把显存占用砍掉一大截。
  • DeepSeek(独创 MLA): DeepSeek 觉得 GQA 还不够省。他们发明了 MLA(Multi-head Latent Attention,低秩多头注意力机制)
    • 硬核原理:传统的 GQA 随着上下文变长,显存里的 KV 缓存依然会线性增加。而 MLA 在计算注意力时,通过矩阵低秩分解(Low-rank Compression)技术,把原本巨大的 KV 矩阵压缩成了一个极小的“特征向量”(Latent Vector)存进显存里。在真正计算的一瞬间,再在显存里动态把它解压放大恢复出来。
    • 效果:DeepSeek-V3 的 MLA 技术,让它的 KV Cache 显存占用直接暴降了 93%。这意味着同样的显卡硬件,DeepSeek 可以容纳超出 Qwen 数倍的并发量(Batch Size)或更长的上下文交互。

加载分词器

1
2
3
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("models/DeepSeek-R1-Distill-Qwen-1.5B")
tokenizer

可以查看models\DeepSeek-R1-Distill-Qwen-1.5B\tokenizer.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
"model": {
// 分词器(Tokenizer)采用的核心算法。
// "BPE" 代表 Byte-Pair Encoding(字节对编码),是目前大模型(如 GPT、Llama、Qwen)最主流的分词算法
"type": "BPE",

// 在分词阶段随机丢弃 Token 的比例(用于某些分词器的训练或正则化)。
// null 代表关闭该功能,确保分词结果是完全确定且唯一的
"dropout": null,

// 未知词标记(Unknown Token)。当遇到词表里完全不认识的字符时,会用这个符号代替。
// null 代表没有设置或不需要(在现代 BPE 分词器中,通常通过字节回退机制来彻底消灭未知词)
"unk_token": null,

// 持续子词前缀。如果一个词被切碎了,后续的碎片可以用这个前缀来标记它们属于同一个词。
// 空字符串 "" 代表不显式添加前缀(Qwen2/Llama 通常通过在 Token 内部保留空格空间来处理词边界)
"continuing_subword_prefix": "",

// 词尾后缀。用来标记一个分词片段是一个完整单词的结尾。
// 空字符串 "" 代表不使用特定的词尾后缀符号
"end_of_word_suffix": "",

// 是否把连续出现的未知词(UNK)融合合并成一个。
// false 代表不融合,每个不认识的字符单独处理
"fuse_unk": false,

// 字节回退机制开关。
// 核心技术点:当设置为 true 时,如果遇到词表里没有的生僻字(比如特殊的emoji),
// 分词器不会报错,也不会把它变成 UNK,而是直接把它拆解成底层的 UTF-8 字节(Byte)符号来兜底,确保全字符覆盖。这里为 false。
"byte_fallback": false,

// 核心词表(Vocabulary 字典)。它是一个庞大的映射表,规定了“文本片段”到“数字 ID”的唯一对应关系
"vocab": {
"!": 0, // 文本中的感叹号,映射到模型内部的数字 ID 是 0
"\"": 1, // 文本中的双引号(经过了转义),映射到模型内部的数字 ID 是 1
"#": 2 // 文本中的井号,映射到模型内部的数字 ID 是 2
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
inputs = tokenizer(
[
"Hello, how are you?", # 输入的第一句话(较短,包含 5 个 Token)
"I'm doing well, thank you!", # 输入的第二句话(较长,包含 7 个 Token)
],

# 1. 填充策略(Padding)
# 作用:强制对齐批次(Batch)内所有句子的长度,让它们变成规整的矩阵(行、列对齐)。
padding=True,

# 2. 返回数据类型(Return Tensors)
# 作用:"pt" 代表 PyTorch Tensor。直接将结果打包成 PyTorch 的张量矩阵,不需要手动转换,方便直接 `.to("cuda")` 送进显卡。
return_tensors="pt",

# 3. 填充方向(Padding Side)—— 本次最核心的修改!
# 作用:强行规定把用来凑数的补零标记([PAD])加在短句子的“左侧”(开头位置)。
# 核心技术原因(大模型推理的铁律):
# 现代大语言模型是自回归生成(从左到右逐字吐字)。
# 如果把 [PAD] 放在右边(默认行为),模型生成新字时,它看到的最新上文就会是 [PAD],这会彻底破坏位置编码(RoPE)以及 Attention 的最新特征,导致模型直接开始胡言乱语或者卡死。
# 把 [PAD] 放在左边,能确保所有句子的“最右端”(即接下来要吐新字的位置)在数学矩阵上完美对齐,而且模型能无缝读到最新的有效上文。
padding_side="left"
).to(model.device)

inputs

image-20260523134035248

padding的作用

模型会把多个句子打包成一个批次(Batch)同时塞给模型同时预测的,但是批量计算要求输入必须是一个完美的矩形矩阵(行和列的数量必须严格相等),所以在句子长度不一的时候,需要设置padding,也就是填充[PAD]

1
2
行1 ──> [   你 ,   叫 ,   什么 ,   名字 ,   呀 ,   ? ]  (原本就最长,无需Padding)
行2 ──> [ [PAD], [PAD], [PAD], [PAD], 南京 , 大学 ] (短句,左边被垫了 4 个 PAD)

分词器还会同时生成一个 attention_mask 矩阵,防止Attention 难被无意义的 [PAD] 干扰

1
2
行1 ──> [ 1, 1, 1, 1, 1, 1 ]  (全看)
行2 ──> [ 0, 0, 0, 0, 1, 1 ] (前4个PAD被强制致盲,Attention 算矩阵时直接把它们当空气)

image-20260523134330284

Padding Side的作用

既然有attentionmask了,为什么还要填充方向(),填充到右边不也行吗

致命原因一:位置编码(RoPE)完全错乱

现代大模型(如 Qwen2、DeepSeek)使用的是 旋转位置编码(RoPE)。模型必须要知道每个 Token 在句子里的绝对位置(它是第几个字),才能理解语法。

假设有两句话同时预测,最大对齐长度是 6:

  • 句子 A 只有 3 个字:“真 漂 亮”
  • 句子 B 有 6 个字:“南京 师范 大学”

❌ 如果填充在右边(Right Padding):

1
2
3
【第1步:输入矩阵】
行 1 ──> [ 真(pos:0) , 漂(pos:1) , 亮(pos:2) , [PAD](pos:3) , [PAD](pos:4) , [PAD](pos:5) ]
行 2 ──> [ 南(pos:0) , 京(pos:1) , 师(pos:2) , 范(pos:3) , 大(pos:4) , 学(pos:5) ]

第一步计算时,有 attention_mask 挡着,句子 A 只看了前 3 个字,成功预测出下一个字是 “啊”

但是,接下来模型要同时生出第 2 个字了(自回归下一步):

新生成的字 “啊” 必须拼到句子的末尾。

  • 句子 B 的 “学” 在位置 5,所以新生成的字在 位置 6
  • 句子 A 的 “亮” 在位置 2,新生成的字 “啊” 应该在 位置 3

灾难发生了:在矩阵计算中,显卡必须整齐划一地把新字拼在矩阵的最后一列(位置 6)

这导致句子 A 的 “啊” 被强行赋予了 位置 6 的位置编码!

在模型眼里,这个句子变成了:“真(0) 漂(1) 亮(2) ...空了3个位置... 啊(6)”。位置编码的断层会直接让自注意力机制彻底报废,模型开始疯狂吐胡话。

如果填充在左边(Left Padding):

1
2
3
【第1步:输入矩阵】
行 1 ──> [ [PAD](pos:0) , [PAD](pos:0) , [PAD](pos:0) , 真(pos:1) , 漂(pos:2) , 亮(pos:3) ]
行 2 ──> [ 南(pos:1) , 京(pos:2) , 师(pos:3) , 范(pos:4) , 大(pos:5) , 学(pos:6) ]

通过特殊的处理,左边的 [PAD] 位置编码直接被无视。

重点看右边:句子 A 的末尾 “亮” 在位置 3,句子 B 的末尾 “学” 在位置 6。

当模型同时吐出新字时,它们都可以规整地统一拼在矩阵的右侧边框(也就是下一列),各自的位置编码能够丝滑地递增(位置 3 变 4,位置 6 变 7),绝对不会发生空间断层。

致命原因二:自回归推理的“长文本记忆(KV Cache)”无法对齐

大模型为了单字蹦字变快,内部有一套叫 KV Cache 的机制——它会把前面已经算过的词的 $K$ 和 $V$ 矩阵存存在显存里,下一次算新字时,直接复用旧的,不重新计算。

显卡在批量(Batch)计算时,要求每一行的记忆长度必须是一致的

❌ 如果 Padding 在右边:

1
2
行 1 (有效记忆3个) ──> [ K1, K2, K3, [PAD], [PAD], [PAD] ] ──> 接下来新字在这里 💥 撞墙了
行 2 (有效记忆6个) ──> [ K1, K2, K3, K4 , K5 , K6 ] ──> 接下来新字在这里

当自回归进行到第二步、第三步时,最新的 Token 需要不断追加到 KV 缓存的末尾。

第一行的末尾被 [PAD] 占着坑,新词进不来;如果强行覆盖 [PAD],第一行的有效长度只有 3,第二行有效长度是 6,这导致 KV 缓存矩阵的各行长度不一致

显卡的核心在面对这种“参差不齐”的动态矩阵时,根本无法做并行的矩阵乘法

如果 Padding 在左边:

1
2
行 1 ──> [ [PAD], [PAD], [PAD], K1, K2, K3 ] ──> 最新插入点 ──> [这里完美对齐]
行 2 ──> [ K1 , K2 , K3 , K4, K5, K6 ] ──> 最新插入点 ──> [这里完美对齐]

因为 [PAD] 稳稳地呆在左边底座,所有句子的动态增长前沿(右侧边缘)是完全对齐的

每蹦出一个新字,所有的句子都可以整齐划一地向右集体拓宽一列。显存里的 KV Cache 矩阵始终是一个标准的、完美的矩形,显卡可以用 $O(1)$ 的极高效率并行吞吐。

模型推理

1
2
3
4
5
6
7
output=model.generate(
input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"],
max_new_tokens=50
)

output

使用模型进行推理后面的向量

1
tokenizer.batch_decode(output)

解码翻译成人类语言

但是目前模型的回答是这样

1
2
["<|end▁of▁sentence|><|end▁of▁sentence|>Hello, how are you? I'm trying to understand some concepts about the electromagnetic spectrum. Could you explain the different parts of it and their relationships? Also, what's the significance of each part?\n\nCertainly! The electromagnetic spectrum is a range of all possible wavelengths of electromagnetic radiation.",
"I'm doing well, thank you! What is the sum of 1/3 and 1/6? Let me think. Hmm, okay, so I need to add these two fractions together. I remember that to add fractions, they need to have the same denominator. The denomin"]

很明显是有问题的,为什么呢

image-20260523135418262

它在预训练阶段(图中的第 1 步)学到的本领是自回归续写(盲猜下一个字)。它习惯了看到普通文章,就顺着普通文章的语法往下编。

但是,现代的对话/推理模型(如 DeepSeek-R1、Qwen2.5-Instruct)是通过微调(图中的第 2 步)训练出来的。为了让它知道什么时候该“闭嘴”、什么时候该“思考(<think>)”、什么时候该“回答”,官方在训练它时,强行给它定了一套严格的特殊符号包围圈

如果你直接把普通的字符串 "Hello, how are you?" 喂给它,对它来说格式是完全陌生的(它找不到应有的标记)。

添加标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
message=[
[
{"role": "user", "content": "Hello, how are you?"}
],
[
{"role": "assistant", "content": "I'm doing well, thank you!"}
]
]
tokenizer.padding_side="left"

inputs=tokenizer.apply_chat_template(
message,
return_tensors="pt",
padding=True,
add_generation_prompt=True
).to(model.device)

tokenizer.batch_decode(inputs["input_ids"])

tokenizer.apply_chat_template是调用models\DeepSeek-R1-Distill-Qwen-1.5B\tokenizer_config.json中的chat_template Jinja2 模板,将对话添加标签

Jinja2 本质上是 Python 编程世界里最著名、使用最广泛的 “文本自动化渲染引擎”(Template Engine)

在代码层面上,它的工作公式非常纯粹:

image-20260523140200484

1
2
['<|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|begin▁of▁sentence|><|User|>Hello, how are you?<|Assistant|><think>\n',
"<|begin▁of▁sentence|><|Assistant|>I'm doing well, thank you!<|end▁of▁sentence|><|Assistant|><think>\n"]

add_generation_prompt=True会在最后添加<|Assistant|><think>\n用来引导模型生成

1
2
3
4
5
6
7
output=model.generate(
input_ids=inputs["input_ids"],
attention_mask=inputs["attention_mask"],
max_new_tokens=256
)

tokenizer.batch_decode(output)

最后再让模型推理,便能正确生成答案了

1
2
['<|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|begin▁of▁sentence|><|User|>Hello, how are you?<|Assistant|><think>\nAlright, the user greeted me with "Hello, how are you?" which is a friendly and open way to start a conversation.\n\nI should respond in a similar positive manner to keep the conversation going smoothly.\n\nI need to make sure my response is welcoming and inviting them to share what they\'re up to.\n\nKeeping it simple and open-ended is key here.\n</think>\n\nHello! I\'m doing well. How can I assist you today?<|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|><|end▁of▁sentence|>',
'<|begin▁of▁sentence|><|Assistant|>I\'m doing well, thank you!<|end▁of▁sentence|><|Assistant|><think>\nOkay, so the user just said, "I\'m doing well, thank you!" Hmm, I need to figure out what they\'re really looking for here. Let me break it down.\n\nFirst, they said, "I\'m doing well, thank you!" That\'s pretty straightforward. They\'re acknowledging their effort and appreciation. But maybe they want more than that? Maybe they\'re feeling a bit overwhelmed or just looking for reassurance.\n\nI should consider different scenarios. Could they be feeling down because of something recent? Or maybe they\'re feeling a bit isolated? Sometimes people appreciate a simple thank you but need more support or a different perspective.\n\nI wonder if they\'re asking for tips on how to handle situations or if they need advice on how to improve. It\'s possible they\'re seeking encouragement or motivation. Alternatively, they might be seeking reassurance that they\'re not alone in feeling this way.\n\nAnother angle is that they might be looking for a way to express gratitude in a different way. Maybe they want to change their approach to communication. Or perhaps they\'re dealing with a specific challenge that\'s making them feel inadequate, and they need validation.\n\nI should also think about the tone they\'re using. They said, "I\'m doing well, thank you!" which is positive and']

为什么会蹦出这么多 <|end▁of▁sentence|>

大模型提前说完了话,它会吐出一个终止符,也就是 <|end▁of▁sentence|>(意为“句子结束”)。

但是,由于这是批量推理,第一句话的通道虽然无话可说了,但第二句话的通道还没写完,显卡不能停,必须硬着头皮继续往后算满剩下的步数。

为了保持矩阵的绝对规整,显卡在接下来的每一步里,只能在第一句的通道后面疯狂重复填充“终止符”来凑数

1
2
3
4
【显卡最终吐出的 output 矩阵物理外观】

第一行 (短句通道) ──> [ Hello! ... assist you today? , <|end...> , <|end...> , <|end...>, ... ] (后面全在机械重复填终止符)
第二行 (长句通道) ──> [ I'm doing well... (模型正兴高采烈地写到一半) ]

因为显卡矩阵在右侧必须对齐,短句子通道在“无话可说”到“等长句子写完”之间的所有空白格,全部被强行塞满了 <|end▁of▁sentence|>

参考资料

本地部署大模型!用Transformers库跑通DeepSeek-R1_哔哩哔哩_bilibili

二分查找

二分查找为什么总是写错?_哔哩哔哩_bilibili

image-20260521135449487

例题:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

注意边界问题

  1. 空数组问题:如果 nums = []n = 0。循环不执行,binary_search_l 会检查 nums[0]binary_search_r 会检查 nums[-1],两者都会直接抛出 IndexError
  2. target 大于数组中所有元素:例如 nums = [1, 2], target = 3binary_search_l 循环结束后 right 会等于 n(即 2),此时检查 nums[right] 会抛出 IndexError
  3. target 小于数组中所有元素:例如 nums = [1, 2], target = 0binary_search_r 循环结束后 left 会等于 -1。在 Python 中 nums[-1] 不会报错而是取最后一个元素,这会导致逻辑错误(如果数组为空则依然会报错)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def searchRange(nums: list[int], target: int) -> list[int]:
n = len(nums)
# 边界问题 1:提前处理空数组
if n == 0:
return [-1, -1]

def binary_search_l(left, right, target):
while left + 1 != right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid
else:
right = mid
# 边界问题 2:right 可能越界到达 n,需要先判断 right != n
if right != n and nums[right] == target:
return right
return -1

def binary_search_r(left, right, target):
while left + 1 != right:
mid = (left + right) // 2
if nums[mid] <= target:
left = mid
else:
right = mid
# 边界问题 3:left 可能越界到达 -1,需要先判断 left != -1
if left != -1 and nums[left] == target:
return left
return -1

# 寻找左边界
left_idx = binary_search_l(-1, n, target)

# 优化:如果左边界都找不到,说明 target 不存在,直接返回,无需再找右边界
if left_idx == -1:
return [-1, -1]

# 寻找右边界(此时可以缩小左边界范围,从 left_idx - 1 开始找,提升微小性能)
right_idx = binary_search_r(left_idx - 1, n, target)

return [left_idx, right_idx]

互联网的基础与组成

核心概念

路由器 (Router)

  • 功能:连接两个或多个不同的计算机网络(如将家庭网络连接到外部互联网)。
  • 作用:它是实现“互联网”连接的关键设备,负责在不同网络之间转发数据。

交换机 (Switch)

  • 功能:将多个网络节点(如电脑、电视、手机)连接起来,组成一个局部的计算机网络。
  • 应用场景:常用于家庭、公司或学校内部组建局域网

image-20260512092631854

ISP

全称:Internet Service Provider(互联网服务提供商)。

通俗理解:它是连接普通用户与互联网的“中介”或“运营商”。如果没有 ISP,我们的手机和电脑就无法访问广域网。

组成部分

image-20260512094913137

功能

image-20260512095248915

电路交换报文交换

1. 电路交换 (Circuit Switching)

这是一种类似于传统打电话的交换方式,必须先“占线”才能通信。

  • 优点:
    • 传输速率高、数据直达: 通信前会建立一条端到端的专用物理通路。
    • 适用场景: 更适合低频次、大量的数据传输(就像打电话一样,拨号花点时间,但一旦接通,长时间通话体验很好)。
  • 缺点:
    • 时间开销大: 建立连接和释放连接都需要消耗额外的时间。
    • 利用率低: 在整个通信期间,线路被双方独占,即使中间有一方没在发数据,别人也用不了这根线。
    • 灵活性差: 线路分配不够灵活,且非常不适合计算机之间常见的“突发式”通信(即高频次、少量的数据传输)。

2. 报文交换 (Message Switching)

这种方式不需要事先占线,而是把整个数据块(报文)传给相邻节点,节点存下来之后再找下一步的路,也就是“存储转发”。

  • 优点:
    • 无需建立连接: 随时有数据随时发,省去了建立连接的时间。
    • 线路利用率高、分配灵活: 用户无需独占整条线路,节点采用“存储转发”的方式动态分配线路。
    • 支持差错控制: 交换节点可以通过校验技术检查数据是否传错。
  • 缺点:
    • 管理不便: 报文的长度是不固定的,这给节点的存储和转发管理带来了麻烦。
    • 资源和时间开销大: 如果报文很长(比如图里手写的 80GB 传到 8bit 链路上),节点需要极大的缓存空间来接收它,并且整体的存储转发时间开销很大
    • 重传代价高: 长报文如果在传输中出现了一点点错误,整个长报文都需要重新传输,代价极其高昂

分组交换

image-20260512101440274

  1. 发送端:拆分与装箱 (拆)

当用户(如小帅 H1)要发送一份很大的“用户数据”给接收者(如小美 H5)时:

  • 切割:数据不会整块发送,而是被切成一个个更小的单位,称为分组 (Packet)
  • 加首部:每个分组前面都会加上“首部”(类似快递单),上面写着源 IP 地址目的 IP 地址
  • 网络中:存储转发 (送)

图中绿色的“分组交换机”(本质上就是路由器)起到了中转站的作用:

  • 存储:路由器先接收整个分组并暂时存入缓存。
  • 查表:读取分组首部的目的地址,查询自己的“路由表”。
  • 转发:决定走哪条路(比如 A→B 或 A→C)最快,然后把分组发往下一个路由器。
  • 特点:每个分组独立选择路径,不一定要走同一条路。
  • 接收端:还原与合并 (合)

当所有分组到达目的地(如小美 H5)后:

  • 剥离:去掉每个分组的首部(撕掉快递单)。
  • 排序重组:按照分组编号,把散乱的数据重新拼装成原始的“用户数据”。
  1. 发送端:拆分与装箱 (拆)

当用户(如小帅 H1)要发送一份很大的“用户数据”给接收者(如小美 H5)时:

  • 切割:数据不会整块发送,而是被切成一个个更小的单位,称为分组 (Packet)
  • 加首部:每个分组前面都会加上“首部”(类似快递单),上面写着源 IP 地址目的 IP 地址
  • 网络中:存储转发 (送)

图中绿色的“分组交换机”(本质上就是路由器)起到了中转站的作用:

  • 存储:路由器先接收整个分组并暂时存入缓存。
  • 查表:读取分组首部的目的地址,查询自己的“路由表”。
  • 转发:决定走哪条路(比如 A→B 或 A→C)最快,然后把分组发往下一个路由器。
  • 特点:每个分组独立选择路径,不一定要走同一条路。
  • 接收端:还原与合并 (合)

当所有分组到达目的地(如小美 H5)后:

  • 剥离:去掉每个分组的首部(撕掉快递单)。
  • 排序重组:按照分组编号,把散乱的数据重新拼装成原始的“用户数据”。

一、 分组交换的核心优点

  • 无需建立连接:发送数据前不需要像打电话(电路交换)那样先拨号,数据直接发出,实时性好
  • 线路利用率高:多个用户的数据可以同时在一条物理线路上交替传输,不会独占资源,非常省钱高效。
  • 灵活分配与容错:路由器根据网络拥堵情况动态选择路径。如果某条路断了,分组可以自动绕道,且支持差错控制,发现坏数据能及时纠正。

二、 横向对比:为什么比“报文交换”好?

报文交换是把整封邮件发出去,而分组交换是拆成小件发。

  • 管理更方便:分组有固定长度上限,路由器更容易管理内存和缓存。
  • 速度更快:由于分组小,路由器处理单个分组的存储转发时间极短,减少了排队等待。
  • 容错代价低:如果传输中出错了,只需要重传出错的那一小块(分组),而不需要重传整个大数据包。

三、 存在的缺点与挑战

  • 额外开销大:每个小分组都要贴上“快递单”(控制信息/首部),当数据被拆得太碎时,这些首部信息的占比会显著增加,降低了纯数据的传输效率。
  • 存在时延:每个路由器都要执行“存储-检查-转发”的过程,这会产生一定的处理时延
  • 处理复杂性
    • 失序:小件快递走不同路径,可能导致“后发的先到”。
    • 丢失:某个分组可能在路上弄丢了。
    • 重复:可能收到两个一样的分组。
    • *注:这些问题都需要接收端的主机通过复杂的协议逻辑来重新排序和处理

计算机网络的分类


网络类型 全称 覆盖范围 典型应用场景 特点/技术
广域网 (WAN) Wide Area Network 几十 ~ 几千公里 跨省、跨国、跨洲 覆盖范围最广,是互联网的核心
城域网 (MAN) Metropolitan Area Network 几千米 ~ 几十千米 一个或几个相邻城市 常采用以太网技术,常并入局域网讨论
局域网 (LAN) Local Area Network 几十米 ~ 几千米 学校、企业、家庭 范围较小,通常使用以太网技术
个域网 (PAN) Personal Area Network 几十米以内 个人设备连接 常通过无线技术连接(如蓝牙),又称WPAN

image-20260512105302052

性能指标

image-20260512112200680

1. 信道 (Channel)

  • 概念: 指向某一个方向传送信息的通道。
  • 核心要点: 信道 $\neq$ 通信线路。一条物理上的通信线路(比如你家插在路由器上的网线),在逻辑上通常包含一条发送信道一条接收信道。这也是为什么我们测速时,会有“上行速率”和“下行速率”两个不同的指标。

2. 速率 (Speed / Data Rate)

  • 概念: 网络节点在信道上每秒传输的数据量,也叫数据率或比特率。
  • 单位: bps (bit/s 或 b/s)。
  • ⚠️ 超级易错点(单位与进位):
    • B 与 b 的区别: 大写 B 代表字节 (Byte),小写 b 代表比特 (bit)。$1\text{B} = 8\text{b}$
    • 网络领域的进位(十进制): 在计算机网络中衡量速率时,前缀 k、M、G、T 的进位是 $10^3$
      • $1 \text{ kbps} = 10^3 \text{ bps}$
      • $1 \text{ Mbps} = 10^6 \text{ bps}$
    • 体系结构/操作系统的进位(二进制): 在衡量内存、硬盘等存储容量时,前缀 K、M、G、T 的进位是 $2^{10}$
      • $1 \text{ KB} = 2^{10} \text{ B} = 1024 \text{ B}$

结合幻灯片的例子: 你家办理了“千兆宽带”,运营商口中的“千兆”指的是 $1000 \text{ Mbps}$(也就是 $10^9 \text{ bps}$)。用迅雷下载文件时,软件显示的单位通常是 MB/s (兆字节/秒)。

所以理论最高下载速度 = $1000 \text{ Mbps} \div 8 = 125 \text{ MB/s}$。

3. 带宽 (Bandwidth)

带宽这个词在不同学科里有两个截然不同的含义:

  • 在《计算机网络》中(数字信号):
    • 含义: 指某信道所能传送的最高数据传输速率(即速率的上限)。
    • 单位: bps (bit/s)。
  • 在《通信原理》中(模拟信号):
    • 含义: 指某信道允许通过的信号频带范围(最高频率与最低频率之差)。
    • 单位: Hz (赫兹)。

1. 时延 (Delay) - 【计算题重灾区】

数据从网络一端传送到另一端所需的总时间。它由四个部分拼凑而成:总时延 = 发送时延 + 传播时延 + 处理时延 + 排队时延

  • 发送时延 (也叫传输时延): 把数据从节点“推”到信道上花的时间。
    • 公式: 数据长度 (bit) ÷ 发送速率 (bit/s)
    • 比喻: 就像火车(数据)驶出站台(网卡)所需的时间。火车越长,驶出站台的时间越久。
  • 传播时延: 数据在物理信道上“跑”完这段距离花的时间。
    • 公式: 信道长度 (m) ÷ 电磁波传播速度 (m/s)
    • 比喻: 火车完全驶出站台后,在铁轨上开到终点站的时间。只和铁轨有多长、火车开多快有关。
  • ⚠️ 超级易混点: 考试时一定要分清这两个!提高网速(带宽)只能减少“发送时延”,无论你办千兆还是万兆宽带,信号在光缆里的“传播时延”(光速)是物理定律,改变不了的。
  • (注:处理时延和排队时延一般在路由器内发生,受网络拥堵影响,基础计算题中如无特别说明常忽略不计。)

image-20260512133345314

2. 时延带宽积 (Delay-Bandwidth Product)

  • 公式: 传播时延 × 带宽
  • 物理含义: 想象信道是一根圆柱形的空心管道,传播时延是管道的“长度”,带宽是管道的“截面积”。时延带宽积就是这个管道的体积
  • 通俗理解: 它代表发送端连续发送数据时,第一个比特刚好到达接收端时,链路上“正在飞”的最大比特数(即管道被塞满时能装多少数据)。

3. 往返时延 (RTT, Round-Trip Time)

  • 含义: 从发送方发送完数据,到发送方收到接收方的“确认回复”,总共经历的时间。我们在电脑上 ping 某个网站弹出的时间就是 RTT。
  • 包含哪些部分:
    • 去程的:单向传播时延
    • 接收方的:处理时延 + 发送时延 (把确认包推上信道)
    • 回程的:传播时延 (确认包跑回来的时间)
  • ⚠️ 易错陷阱: RTT 不包含 发送方发送业务数据本身的“发送时延” ($t_1$)!它只从数据“发完”的那一刻开始掐表。

image-20260512134726893

4. 信道利用率

  • 含义: 信道有多大比例的时间是在传数据的(没有空闲)。
  • 辩证关系:
    • 太低:修了八车道高速公路却没几辆车,浪费资源。
    • 太高:容易导致网络拥塞。就像节假日的高速公路,利用率接近 100% 时,车会堵死,排队时延会急剧增加

分层结构

1. 数据的传输视角:水平与垂直

理解网络体系结构,需要同时具备“水平”和“垂直”两种思维:

  • 水平视角(逻辑通信): 每一层的实体(比如两台电脑的应用层)都认为自己在和对端的同一层进行直接通信。它们之间交流的规则就是“协议”。为了让对端看懂自己的意图,发送方会在数据前加上专门的“首部”(有时还有“尾部”)。
  • 垂直视角(物理实现): 数据真正在主机内部的流动是自上而下(发送方)和自下而上(接收方)的。发送方每往下一层,就套上一层新的“首部”(这叫封装,像套娃一样);到了最底层的物理层,所有数据变成单纯的 01 比特流在网线里跑;接收方则逐层剥离首部(这叫解封装),把原始数据还原给上层。
    • 注意图中的小细节: 只有第2层(数据链路层)不仅加了首部 $H_2$,还加了尾部 $T_2$。

image-20260512143350054

image-20260512143200180

2. 三个重要的数据单元名词 (SDU, PCI, PDU)

这是考试中最爱考概念辨析的地方,你需要记住它们的全称和包含关系:

  • SDU (Service Data Unit, 服务数据单元): 上一层传下来的、需要本层帮忙传输的原始数据(Payload)
  • PCI (Protocol Control Information, 协议控制信息): 本层为了实现协议功能而自己添加的“首部/尾部”信息
  • PDU (Protocol Data Unit, 协议数据单元): 本层最终打包好的、准备传给下一层的完整数据块

核心换算公式:

第 $n$ 层的完整数据块,等于本层加的控制信息加上层传下来的数据:

$n\text{-PDU} = n\text{-PCI} + n\text{-SDU}$

第 $n$ 层打包好的数据,到了第 $n-1$ 层眼里,统统变成了需要传输的原始数据:

$n\text{-PDU} = (n-1)\text{-SDU}$

image-20260512143215280

3. 协议的三要素

网络协议是水平的,它约束了通信双方必须遵守的规则。一个完整的协议必须包含以下三个要素:

  1. 语法 (Syntax): 规定了数据和控制信息的结构与格式。比如:报文的首部占多少个字节?第1个字节代表什么意思?
  2. 语义 (Semantics): 规定了通信双方需要“做什么动作”或“表达什么意思”。比如:这串代码是代表“请求建立连接”,还是代表“数据接收成功(ACK)”?
  3. 同步/时序 (Synchronization/Timing): 规定了事件实现的顺序和条件。比如:发送方发完数据后,如果在 10 秒内没收到对方的确认,就需要重新发送

OSI 七层参考模型

image-20260512154222954

层次 (从下到上) 英文简称 数据传输单位 核心任务与功能 典型网络设备
1. 物理层 Physical 比特 (Bit) 实现相邻节点间比特流的透明传输;定义接口的物理/电气特性(如电压代表0还是1)。 集线器 (Hub)、中继器
2. 数据链路层 Data Link 帧 (Frame) 确保相邻节点间的逻辑无差错;进行差错控制(校验、纠错/丢弃)、流量控制。 交换机 (Switch)、网桥
3. 网络层 Network 分组/数据报 (Packet) 将分组从源节点路由转发到目的节点;维护路由表、拥塞控制、网际互联。 路由器 (Router)
4. 传输层 Transport 报文段 (Segment) 实现端到端(进程到进程,认准“端口”)的通信;复用与分用;全局差错/流量控制。 -
5. 会话层 Session 报文 (Message) 管理进程间会话;使用“检查点”机制实现断点续传。 -
6. 表示层 Presentation 报文 (Message) 处理数据格式转换(如编码转换、数据压缩/解压加密/解密)。 -
7. 应用层 Application 报文 (Message) 实现特定的网络应用(如微信、浏览器等)。 主机 (端系统)

⚠️ 易混淆的“重灾区”提醒:

在这几张图中,老师用红笔重点圈出的几个细节,是考试最喜欢挖坑的地方:

1. 差错控制:数据链路层 vs 传输层

  • 数据链路层的差错控制是“局部”的,它只保证这段网线(相邻两个节点A到B)传的数据没错,错了就扔掉。
  • 传输层的差错控制是“全局”的,它要保证从发送方主机一路经过无数路由器,最终到达接收方主机的数据是完整、有序、不丢失的。

2. 设备的工作层次(图2)

  • 路由器是“三层设备”:它最高只能理解到网络层(拆开看分组首部里的 IP 地址)。
  • 交换机是“二层设备”:它最高只能理解到数据链路层(拆开看帧首部里的 MAC 地址)。
  • 集线器是“一层设备”:它就是个无脑的物理放大器,只管电信号的 0 和 1。
  • 只有你使用的手机和电脑(主机)才拥有完整的 1 到 7 层。

3. 通信范围的本质区别

  • 网络层(IP):解决的是“主机到主机”的通信(帮你把包裹送到收件人的小区)。
  • 传输层(端口):解决的是“进程到进程”的通信(不仅送到小区,还要具体交给收件人本人,比如是给微信的还是给 QQ 的)。

image-20260512154120967

TCP/IP 模型

image-20260512164948826

1. 模型结构对比

  • OSI 参考模型(7层):应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
  • TCP/IP 模型(4层):应用层、传输层、网络层、网络接口层。
  • 层级映射关系
    • TCP/IP 的应用层整合了 OSI 的应用层、表示层(数据格式转换)和会话层(会话管理)。如果应用需要格式转换或会话管理,由应用自身实现。
    • TCP/IP 的网络接口层整合了 OSI 的数据链路层和物理层。TCP/IP 并没有具体规定这一层的功能和协议,而是交由网络设备商自由发挥。

2. 各层的数据传输单位与核心功能

  • 应用层(单位:报文):实现特定的网络应用。
  • 传输层(单位:报文段):实现端到端(进程到进程)的通信。负责复用和分用、差错控制、流量控制、连接管理等。
  • 网络层(单位:数据报/分组):把分组从源结点转发到目的结点。核心功能是路由选择分组转发
  • 数据链路层(单位:帧):确保相邻节点之间的链路逻辑上无差错(主要负责差错控制、流量控制)。
  • 物理层(单位:比特):实现相邻节点之间的比特流传输,定义电路接口参数、电气特性等。

3. TCP/IP 模型中的“背锅”机制(网络层与传输层的协作)

课件通过生动的比喻(“摆烂”和“背锅”)解释了 TCP/IP 的设计理念:

  • TCP/IP 的网络层(摆烂):只提供“尽最大努力交付”,数据传输是不可靠的。它只管路由和转发,不负责差错控制和流量控制。
  • TCP/IP 的传输层(背锅):因为网络层不可靠,所以保证数据传输正确性和可靠性的重任就交给了传输层。传输层必须负责差错控制、流量控制和可靠传输管理。

4. 核心考点总结:OSI vs TCP/IP 服务的差异

最后一张图片总结了两个模型在网络层和传输层提供服务时的关键区别,这是考试/面试的重点:

比较维度 OSI 参考模型 TCP/IP 模型
网络层服务 可提供无连接不可靠服务(数据报),也可提供有连接可靠服务(虚电路)。 提供无连接不可靠的服务(数据报/尽最大努力交付)。
传输层服务 向应用层提供有连接的可靠服务。 可提供有连接可靠服务(TCP协议),也可提供无连接不可靠服务(UDP协议)。

总结一句话: OSI 模型在设计时希望网络层能做更多可靠性的工作,而传输层相对单纯;TCP/IP 模型则极其务实,让底层的网络层“轻装上阵”(不可靠、无连接),将复杂的可靠性保障全部推给了上面的传输层去实现。

物理层

通讯原理基本概念

1. 信号的分类

  • 数字信号:信号的值是离散的(如常见的方波,用高低电平表示 0 和 1)。
  • 模拟信号:信号的值是连续的(如连续波动的正弦波)。

QQ20260512-164906

2. 通信系统的基本模型(五大要素)

  • 数据:信息的实体(如文字、声音、图像),在计算机内部通常以二进制表示。
  • 信号:数据的载体。数据必须转换为信号才能在信道中传输。
  • 信源:信号的来源(即数据的发送方,如计算机 A)。
  • 信宿:信号的“归宿”(即数据的接收方,如路由器或计算机 B)。
  • 信道:信号传输的通道。注意:一条物理线路通常包含两条信道(发送信道和接收信道),以实现双向通信。

QQ20260512-164922

3. 码元(Symbol)的概念

  • 定义:在一个“信号周期”内出现的某一种特定状态的信号,称为一个码元。
  • 多进制码元
    • 如果一个周期内信号有 4 种可能的取值(如 4 种不同的电压:2V, 1V, -1V, -2V),则称为 4进制码元,每个码元可以携带 2 个比特(2 bit)的数据(对应 00, 01, 10, 11)。
    • 同理,如果信号有 8 种状态,则是 8进制码元,每个码元携带 3 bit 数据。
  • 模拟信号设计:不仅数字信号可以设计多进制,模拟信号也可以通过改变波的特征(如不同的频率或相位组合)来设计出 2进制、4进制甚至更多进制的码元。
  • 优缺点:采用多进制码元的优点是每个信号周期能传输更多信息,提升传输效率;代价是需要更强的信号功率,且对信道质量(抗干扰能力)要求更高。

QQ20260512-165615

4. 核心公式:码元与比特的关系

这是计算网络传输速率的重要考点,描述了一个码元可以携带多少比特的数据:

  • 公式:如果一个信号周期内可能出现 $K$ 种信号(即 $K$ 进制码元),则:

  • 举例

    • 4 种信号 ($K=4$):$\log_2 4 = 2 \text{ bit}$
    • 8 种信号 ($K=8$):$\log_2 8 = 3 \text{ bit}$
    • 32 种信号 ($K=32$):$\log_2 32 = 5 \text{ bit}$

奈氏准则(无噪声情形)和香农定理(有噪声情形)

1. 带宽(Bandwidth)的两种语境

虽然本质都是指“信道传输数据的能力”,但在不同学科中定义和单位有所不同:

  • 在《计算机网络》中:表示信道所能通过的“最高数据率”。单位是 bps(比特/秒)。
  • 在《通信原理》中:表示信道允许通过的信号频带范围。单位是 Hz(赫兹)。

2. 噪声与信噪比(SNR)

  • 噪声:会对信道产生干扰,从而影响数据的传输效率。
  • 信噪比:信号平均功率与噪声平均功率的比值。有两种表示方式:
    • 无单位记法:$S/N = \frac{\text{信号的功率}}{\text{噪声的功率}}$
    • 分贝(dB)记法:$\text{信噪比(dB)} = 10 \log_{10}(S/N)$

3. 奈奎斯特定理(奈氏准则)—— 针对“无噪声”信道

该定理用于计算在理想低通信道(没有噪声、带宽有限)下的极限传输速率。

  • 极限波特率(码元传输速率):$2W$ (单位:波特/Baud,即 码元/秒)

    • $W$ 是信道的频率带宽(单位:Hz)。
  • 极限比特率(数据传输速率):结合“1个码元可携带 $\log_2 K$ bit 数据”的公式,推导出极限比特率为:

    • $K$ 是一个“信号周期”内可能出现的信号状态种数(即多少进制的码元)。

4. 香农定理 —— 针对“有噪声”信道

该定理用于计算在实际信道(有噪声、带宽有限)下的极限传输速率。

  • 极限比特率公式

    • $W$ 是信道的频率带宽(单位:Hz)。
    • $S/N$ 是信噪比。
  • ⚠️ 核心易错点提示:在使用香农定理计算时,公式中的信噪比 $S/N$ 必须使用“无单位记法”的比值形式。如果题目给出的是分贝(dB),必须先通过公式逆运算换算成无单位的比值后,才能代入香农公式计算。

编码与调制

QQ20260512-222444

image-20260519103945343

QQ20260512-221124

image-20260519104003426

传输介质

image-20260513084325475

image-20260513084342463

image-20260513084358459

image-20260513084435913

物理层设备

1. 中继器(Repeater)的工作原理

  • 核心功能:中继器只有两个端口。它的主要作用是接收衰减、失真的信号,将其整形再生(恢复成标准的电平信号),然后再转发到另一个端口,从而延长物理传输距离。
    • 示例:如果标准低电平是 0.5~1.5V,中继器收到失真的低电平信号后,会将其重新整形为标准的 1V 再输出。
  • 通信限制:仅支持半双工通信,即两端的节点不能同时发送数据,否则会导致信号“冲突”。
  • 网段概念:中继器的两个端口对应两个不同的“网段”。

2. 集线器(Hub)与冲突域

  • 本质:集线器可以看作是一个“多端口的中继器”。
  • 冲突域(碰撞域):连接在同一个集线器上的所有主机处于同一个冲突域。这意味着如果两台主机同时发送数据,信号就会在集线器内部发生冲突。
  • 信道争用:处于同一冲突域的主机,在发送数据前必须进行“信道争用”(抢占发送权)。

3. 集线器组网的关键特性

  • 拓扑结构差异
    • 物理上是“星形”拓扑:各台主机通过网线汇聚连接到中心的集线器上。
    • 逻辑上是“总线型”拓扑:数据在集线器内部是“广播式”传输的(发给一个节点,其他节点都能收到),工作机制与早期共享一根总线的网络完全一样。
  • 共享带宽:集线器连接的所有设备共享该集线器的总带宽。
    • 示例:如果一个集线器的总带宽是 10Mbps,连接了 8 台主机同时工作,那么每台主机平均只能分到 1.25Mbps 的带宽。

4. 中继器与集线器的通用限制

  • 不能“无限串联”:为了保证信号的有效传输和冲突检测,串联数量有严格限制。
    • 5-4-3 原则(以 10Base5 网络为例):最多只能串联 5 个网段,中间使用 4 个集线器(或中继器),并且这 5 个网段中只有 3 个网段可以挂接计算机。
  • 速率向下兼容:如果一个集线器连接了不同速率的网段(或网卡),会导致整个网络的所有网段强制“向下兼容”,以最低的那个速率运行。
  • 介质兼容性:集线器两端可以连接不同的传输介质,这意味着它连接的网段在物理层面的接口特性(物理层协议)是可以不同的。

image-20260513085749075

数据链路层

功能

1. 数据链路层所处的地位

  • 核心作用:数据链路层位于网络层和物理层之间,负责将网络层交下来的IP数据报(分组)加上首部和尾部,封装成帧
  • 服务关系
    • 向下:使用物理层提供的“比特传输”服务。
    • 向上:为网络层提供服务,负责将封装好的帧传输给下一个相邻结点(如从主机传到路由器)。
  • 两个核心概念辨析
    • 物理链路:由底层传输介质(0层)和物理层(1层)共同实现,是相邻结点间单纯的物理连接线路。
    • 逻辑链路(数据链路):数据链路层基于“物理链路”,通过相关协议实现了相邻结点间逻辑上无差错的数据传输通道。

2. 数据链路层的功能(五大核心)

第二张图以思维导图的形式概括了数据链路层必须完成的具体任务:

  • 封装成帧(组帧)
    • 帧定界:确保接收方能够准确识别出一个完整帧的界限(哪里开始,哪里结束)。
    • 透明传输:接收方的链路层能从收到的帧内无损恢复原始数据(SDU),让上方的网络层“感受不到”底层曾把数据切分并封装成帧的过程。
  • 差错控制
    • 专门用于发现并解决帧内部的“位错”(即具体的0、1比特发生翻转)。
    • 两种解决思路:一是接收方检错后丢弃该帧,让发送方重传(需检错编码);二是由接收方直接发现并自行纠正错误(需纠错编码)。
  • 可靠传输
    • 专门用于发现并解决宏观的“帧错”,包含三种典型情况:
      • 帧丢失:例如发 1234,只收到 124。
      • 帧重复:例如发 1234,收到了 12334。
      • 帧失序:例如发 1234,收到了 1324。
  • 流量控制
    • 控制发送方发送帧的速率,防止发送过快导致接收方“来不及”接收和处理。
  • 介质访问控制
    • 广播信道:必须实现此功能。因为广播信道逻辑上是总线型拓扑,多个结点需要“争抢”传输介质的使用权,必须有机制来协调。
    • 点对点信道:通常不需要此功能,因为两点之间有专属的传输介质,不存在信道争用问题。

QQ20260513-090143

组帧

1. 字符计数法

  • 原理:在每个帧的开头,使用一个固定长度的数字字段来表示该帧的总长度(注意:帧长 = 计数字段长度 + 数据部分长度)。
  • 最大缺陷(健壮性差):如果传输过程中由于干扰导致计数字段出错(例如原本长度是7变成了3),接收方就会找错边界,不仅当前帧报废,还会导致后续所有帧都无法正确定界,产生连锁反应。

image-20260513092318404

2. 字节填充法(特殊字符法)

  • 帧定界:使用特殊的控制字符来标记帧的开始(如 SOH - Start of Header)和结束(如 EOT - End of Transmission)。
  • 透明传输:如果帧的数据部分恰好包含了与 SOHEOT 相同的比特序列,发送方会在这些特殊字符前面插入一个“转义字符 ESC”。接收方在读取时如果遇到 ESC,就会知道紧跟其后的字符是普通数据,并在处理时将 ESC 删掉(逆向处理)。

image-20260513092345270

3. 零比特填充法

  • 帧定界:使用特殊的比特串 01111110 作为标记帧开始和结束的标志。
  • 透明传输(核心规则)
    • 发送方:对数据部分进行扫描,每当遇到连续的 5 个 1,就强制在后面填充一个 0。这样就绝对避免了数据段出现连续 6 个 1(即伪造的定界符)。
    • 接收方:对收到的数据部分进行逆处理,每当遇到连续的 5 个 1,就自动删掉紧随其后的 1 个 0,从而恢复原始数据。
  • 应用:HDLC协议和PPP协议均采用此方法。

image-20260513092358876

4. 违规编码法

  • 原理:利用物理层传输中“不合法的(违规)”信号电平来表示帧的开头和结尾。这种方法需要物理层的配合。
  • 示例:在标准的曼彻斯特编码中,每个比特周期的中间必须有电平跳变(上跳表示0,下跳表示1)。如果人为制造一个“周期中间不跳变”的信号,这就是一个违规信号,接收方就可以用这个特殊的不跳变信号来作为帧的定界符

image-20260513092412375

检错编码

这些图片是一份关于计算机网络数据链路层差错控制(检错编码)\的详细教学课件,重点讲解了两种常见的检错码:**奇偶校验码**和*循环冗余校验码(CRC)*

以下是核心知识点的详细总结:

1. 奇偶校验码 (Parity Check)

  • 基本原理:在原有的“有效信息位”附加 1 位的“奇偶校验位”,使得整个校验码中数字“1”的个数满足奇数或偶数的要求。
    • 奇校验:整个校验码中“1”的个数为奇数。
    • 偶校验:整个校验码中“1”的个数为偶数。
  • 硬件实现(以偶校验为例):将各信息位进行“异或(模2加)”运算(即相同为0,相异为1),得到的结果就是偶校验位的值。
  • 致命缺陷:如果传输过程中发生位错的比特数是偶数个(比如2位、4位同时翻转),奇偶性不会改变,此时无法检测出错误

image-20260513110906416

image-20260513110915033

2. 循环冗余校验码 (CRC, Cyclic Redundancy Check)

CRC 是一种检错能力极强的编码方式,广泛应用于实际网络中。

  • 核心思想:发送方和接收方事先约定一个“除数”。发送方在数据后面添加若干位“校验位”(即余数),使得拼接后的完整数据能够被这个约定的“除数”整除。接收方收到数据后,用同样的“除数”去除,如果余数为 0,说明传输无误;若余数非 0,说明数据出错。
  • 运算规则:CRC 的计算使用的是“模2除法”,其本质是按位进行异或运算(不发生进位和借位)。

💡 CRC 计算的四大步骤(以课件例题为例):

例:生成多项式 $G(x) = x^3 + x^2 + 1$,信息码为 101001

  1. 确定“除数”和位数 $R$
    • 将生成多项式的系数提取出来,转换为二进制“除数”。$x^3+x^2+1$ 对应二进制为 1101
    • 确定校验位的长度 $R$。$R$ 等于生成多项式的最高次幂,即 $R = 3$(除数位数减1)。
  2. 移位(补零)
    • 将信息码左移 $R$ 位,即在信息码后面补上 $R$ 个 0,作为“被除数”。本例中变为 101001000
  3. 相除(模2除法)

    • 用被除数 101001000 模2除以除数 1101
    • 经过一步步异或运算,最后得到的 $R$ 位余数即为 CRC 校验码(FCS)。本例中余数为 001。4

    image-20260513110952903

  4. 生成发送帧与检错

    • 发送方:将余数拼接在原始信息后,实际发送的数据为 101001001

    • 接收方:收到数据后,用 1101 进行模2除法,若最终余数为 000,则代表没有出错。

image-20260513111052010

3. CRC 的检错能力与特点

  • 理论上,只要生成多项式选择得当,CRC 可以检测出:所有奇数个错误、所有双比特错误、以及所有长度小于等于校验位长度的连续错误。
  • 在特定条件下(如 $2^R \ge K+R+1$),CRC 码可以具备纠正 1 位错的能力,但在计算机网络的实际应用中,CRC 一般只用来“检错”,发现错误直接丢弃重传,而不进行复杂的纠错

image-20260513111109881

海明校验码(Hamming Code)

1. 海明码的核心设计思路与长度计算

  • 设计原理:将信息位进行分组,并为每组提供一个偶校验位。通过多个校验位的组合,不仅能检测出错误,还能直接“指引”出发生错误的具体位置。

  • 校验位数量公式:假设有 $n$ 个信息位,需要添加 $k$ 个校验位。为了让这 $k$ 个校验位能指示出所有 $(n+k)$ 个位置的错误状态以及 1 种正确状态,必须满足不等式:

    (例如:课件中 4位信息位 $n=4$,代入公式计算得出至少需要 $k=3$ 个校验位)

2. 海明码的求解步骤(以信息位 1010 为例)

构建海明码主要分为三步:

  • Step 1:确定位置分布
    • 校验位 $P_i$ 必须放在海明位号为 $2^{i-1}$ 的位置上(即 $H_1, H_2, H_4, H_8 \dots$)。
    • 信息位 $D_i$ 按照顺序填入剩下的空缺位置(如 $H_3, H_5, H_6, H_7$)。
  • Step 2:确定校验分组
    • 每个校验位负责校验海明位号二进制表示中特定位为 1 的集合。
    • $P_1$ 负责校验位号二进制最低位1 的位置($H_1, H_3, H_5, H_7$)。
    • $P_2$ 负责校验位号二进制次低位1 的位置($H_2, H_3, H_6, H_7$)。
    • $P_3$ 负责校验位号二进制第三位1 的位置($H_4, H_5, H_6, H_7$)。
  • Step 3:计算校验位的值(偶校验)
    • 利用异或运算($\oplus$)求解,确保每组内的 1 的个数为偶数。
    • 计算得出 $P_1 = 0$, $P_2 = 1$, $P_3 = 0$。拼接得到最终的 7位海明码。

3. 海明码的检错与纠错机制

接收方收到数据后,需要计算校验方程(Syndrome) $S_1, S_2, S_3$:

  • 计算方法是将接收到的各分组数据再次进行异或运算。
  • 判定规则
    • 如果 $S_3 S_2 S_1 = 000$,说明传输无错误
    • 如果 $S_3 S_2 S_1 \neq 000$,其二进制值直接对应出错的海明位号。(例如:计算出 010,对应的十进制是 2,说明第 $H_2$ 位出错了,只需将该位翻转即可纠正)

4. 补充考点:全校验位(解决 1位错与 2位错的区分)

标准的海明码具有“纠 1位错,检 2位错”的能力,但仅凭 $S_3 S_2 S_1$ 无法区分到底是发生了 1位错还是 2位错。

  • 解决方案:在最高位(如 $H8$)增加一个“全体偶校验位”($P{\text{全}}$),对前面的所有位进行整体偶校验。
  • 综合判定逻辑
    1. $S_3 S_2 S_1 = 000$ 且 整体校验成功 $\to$ 无错误
    2. $S_3 S_2 S_1 \neq 000$ 且 整体校验失败 $\to$ 有 1位错(直接用 $S$ 的值定位并纠正)。
    3. $S_3 S_2 S_1 \neq 000$ 且 整体校验成功 $\to$ 有 2位错(此时海明码失效,无法纠正,必须要求重传

QQ20260513-100924

QQ20260513-101157

滑动窗口机制

QQ20260513-112016

QQ20260513-112118

停止等待协议

1. 停止-等待协议的核心机制 (图1)

该协议的运行依赖于四大机制:

  • 滑动窗口机制:发送窗口大小 $W_T=1$,接收窗口大小 $W_R=1$。即“发送方发一个,等一个;接收方收一个,确认一个”。
  • 确认机制:如果接收方收到 $i$ 号帧且没有检测出差错,必须给发送方返回一个确认帧($ACK_i$)。
  • 重传机制(超时重传):发送方发出数据后会启动计时器。如果超时仍未收到对应的 $ACK_i$,则认为数据丢失,会重新发送 $i$ 号帧。
  • 帧编号:为了区分不同的帧,需要给帧编号。在 S-W 协议中,由于窗口大小都是 1,仅需 1 bit(即 0 和 1 交替使用)即可满足编号要求。

2. 帧的交互与序号概念 (图2)

  • 发送方与接收方交替使用 01 作为帧序号(如 Data0, Data1, Data0…)。
  • 帧结构:数据帧包含首部、可长可短的数据部分和尾部;而确认帧(ACK帧)通常只有首尾的控制信息(如帧序号、帧类型),数据部分非常短甚至为空。
  • 正常交互过程:发送方发送 Data0,接收方收到后回复 ACK0;接着发送方再发送 Data1,以此类推。

QQ20260513-112732

3. 异常处理:超时重传 (图3)

图片演示了网络中出现异常的情况:

  • 如果发送方发送了帧 C(序号为0),但在计时器超时前没有收到对应的确认帧(可能是数据帧在路上丢失,也可能是ACK帧丢失或迟到)。
  • 发送方的时间一到,就会触发“超时重传”机制,重新发送一遍序号为 0 的帧 C。

4. 核心探讨:为什么要给帧编号? (图4)

这是该协议的一个重要考点:

  • 如果没有帧序号会发生什么?

    如果发生超时重传,接收方再次收到同一个帧时,将无法判别这是一个新到达的数据帧,还是一个“重复帧”(即由于之前自己发的ACK丢了,发送方重新发来的老数据)。

  • 为什么 1 bit 就够了?

    因为在 S-W 协议中,接收窗口和发送窗口的距离不超过 1,新帧和旧帧只会在相邻的两次传输中产生混淆,因此用 0 和1 交替表示(1 bit)就已经足够区分了。

后退 N 帧协议(Go-Back-N, 简称 GBN 协议)

1. GBN 协议的核心机制

  • 滑动窗口大小:发送窗口 $W_T > 1$(可以连续发送多个数据帧而无需等待确认),接收窗口 $W_R = 1$(接收方只能按顺序依次接收一个帧)。
  • 累积确认(Cumulative ACK):这是 GBN 的特殊规则。接收方不需要每收到一个帧就发一个确认,它可以连续收到多个数据帧后,只发送最后一个正确按序到达帧的确认帧(ACK_i)ACK_i 的含义是:“序号 $i$ 及其之前的所有帧我都已经正确收到了”。
  • 超时重传(后退 N 帧):如果发送方的计时器超时未收到 ACK_i,发送方不仅要重传第 $i$ 号帧,还必须重传第 $i$ 号帧之后已经发送出的所有帧。这就是“后退 N 帧”名字的由来。

QQ20260514-082558

2. 异常情况示例与处理

  • 数据帧丢失 / 失序到达

    • 假设发送方连续发送了 0、1、2 号帧,但 1 号帧在路上丢失了。
    • 接收方正确收到了 0 号帧(接收窗口向前移动,期待 1 号帧)。接着接收方收到了 2 号帧。
    • 由于 2 号帧超出了当前 $W_R = 1$ 的接收窗口(接收方只想要 1 号),2 号帧被视为“非法帧”并被直接丢弃。
    • 接收方会再次向发送方返回 ACK0(目前已按序接收的最后一个正确帧),提醒发送方:“我还在等 1 号帧”。
    • 最终发送方超时,被迫“后退”,重新发送 1 号帧和 2 号帧。

    image-20260514083851115

  • 确认帧(ACK)丢失

    • 如果前面的 ACK 丢失,但后续的 ACK 成功到达,由于“累积确认”机制,发送方不会重传。
    • 如果一段时间内没有任何 ACK 到达导致超时,发送方会重置计时器,并重传该窗口内未被确认的所有数据帧。

3. 窗口尺寸的强制约束

为了让接收方能正确区分新帧和旧帧(避免重复帧被当成新帧接收),帧序号的比特数 $n$ 与窗口大小必须满足严格的数学关系:

  • 公式:$W_T + W_R \le 2^n$
  • 反面探讨:如果采用 2bit 编号(序号为 0, 1, 2, 3,即 $2^n = 4$),在 $W_R = 1$ 的情况下,发送窗口 $W_T$ 最大只能是 3。如果强行设置 $W_T = 4$,当发送方发出 0, 1, 2, 3 号帧且接收方全部收到后,接收方期待下一个新 0 号帧。若此时 ACK 丢失导致发送方超时重传旧 0 号帧,接收方会将其错误地当作新数据接收,导致严重的数据错乱。

QQ20260514-083228

4. GBN 协议的优缺点

  • 优点:相比停止-等待协议,允许连续发送多个帧,提高了传输效率。
  • 缺点:如果信道误码率很高或接收方处理慢,一旦中间某个帧出错,发送方必须把后面哪怕已经正确传达的帧也全部重传一遍,导致传输效率大幅下降

QQ20260514-083247

选择重传协议(Selective Repeat, 简称 SR 协议)

1. SR 协议的核心机制与革新

  • 滑动窗口机制:发送窗口 $W_T > 1$,接收窗口 $W_R > 1$。这是与 GBN 最大的区别。接收窗口大于 1 意味着接收方可以缓存失序到达的帧,而不会像 GBN 那样直接丢弃。
  • 确认机制(一帧一确认):SR 协议不再使用累积确认。接收方收到哪个帧,就单独为那个帧发送确认帧($ACK_i$)。
  • 否定确认(NAK):如果接收方发现某个帧有差错(如校验和错误),不仅会丢弃它,还会主动向发送方返回一个否定确认帧($NAK_i$),要求发送方立即重传该帧,而不需要死等计时器超时。
  • 选择重传:当发送方发生超时或收到 $NAK_i$ 时,仅需重传那一个出错或丢失的 $i$ 号帧,无需重传后续已经发送的帧。

2. 异常情况处理示例

  • 数据帧丢失:发送方发出 0~5 号帧,其中 5 号帧丢失,6、7、0 号帧失序到达接收方。由于接收窗口够大,接收方会缓存 6、7、0 号帧并分别返回 ACK。当发送方的 5 号帧超时后,发送方仅重传 5 号帧
  • 数据帧出错(被丢弃):5 号帧在传输中发生位错,接收方检测出差错后将其丢弃,并主动发送 NAK 5。发送方收到 NAK 5 后,立即请求重传 5 号帧(无需等待超时)。

QQ20260514-085126

  • 确认帧(ACK)丢失:如果某个帧的 ACK 丢失导致发送方超时重传,接收方会再次收到该帧。接收方发现这是一个落在当前接收窗口之外(或者在窗口内但已被标记接收)的重复帧,接收方会直接丢弃该帧,但必须重新返回一次该帧的 ACK,以让发送方能向前滑动窗口。

3. 窗口尺寸的强制约束

为了保证发送方和接收方能正确识别新帧和旧帧(避免重复帧落入新窗口被错误接收),SR 协议对窗口大小有严格的数学要求:

  1. 最大序号限制:$W_T + W_R \le 2^n$ ($n$ 为帧序号的比特数)。
    • 课件探讨:如果用 3bit 编号(0~7),若 $W_T=5, W_R=4$(不满足公式),当 0~4 号帧的 ACK 全部丢失,发送方超时重传旧的 0 号帧时,接收方窗口已经滑动到了等待新 0 号帧的位置,此时接收方会错误地将“旧 0 号帧”当成“新 0 号帧”接收。
  2. 收发窗口比例:通常要求 $W_R \le W_T$(接收窗口不能大于发送窗口,因为没必要)。在实际应用中,通常取两者的最大允许值,即 $W_T = W_R = 2^{n-1}$

QQ20260514-085845

协议信道利用率

1. 核心概念与符号定义

在计算信道利用率时,一个完整的“发送-确认”周期(Cycle)包含以下几个时间分量:

  • $T_D$:数据帧的传输时延(发送时延)。即把数据比特流推送到链路上所需的时间。
  • $RTT$:往返传播时延。等于 $2 \times$ 单向传播时延,即信号在物理介质中跑一个来回的时间。
  • $T_A$:确认帧(ACK)的传输时延(发送时延)。在很多考题中,由于 ACK 帧很短,这个时间常被忽略(视为 0)。

一个完整的周期时间 = $T_D + RTT + T_A$

2. S-W 协议的信道利用率

  • 工作机制:发送方每发送 1 个数据帧,就必须停下来等待确认,收到 ACK 后才能发送下一个帧。

  • 理想利用率公式

  • 物理意义:在一个长长的周期时间里,信道只有在 $T_D$ 这段时间是在真正传输有效数据的,其余时间都在“空等”,因此信道利用率往往非常低

  • 课件示例:$U = \frac{4}{4 + 2 \times 7 + 1} \approx 21\%$

QQ20260514-090835

3. GBN 与 SR 协议的信道利用率(滑动窗口协议)

  • 工作机制:发送方拥有大小为 $N$ 的发送窗口,可以连续不断地向信道中“注入” $N$ 个数据帧,而无需等待每个帧的 ACK。

  • 理想利用率公式

  • 物理意义:由于连续发送了 $N$ 个帧,在一个周期内的有效数据发送时间变长了($N \cdot T_D$),因此信道利用率得到了大幅提升。注意:信道利用率的极限是 1(即 100%),不能超过这个值。

  • 课件示例:$N=4$ 时,$U = \frac{4 \times 4}{4 + 2 \times 7 + 1} \approx 84\%$

QQ20260514-091433

4. 易考点与术语补充

  • 窗口大小对利用率的影响:对于采用 $n$ 比特进行帧编号的系统,必须满足 $W_T + W_R \le 2^n$。
    • GBN 协议的接收窗口 $W_R = 1$。
    • SR 协议的接收窗口 $W_R > 1$。
    • 因此,在相同的比特位 $n$ 下,GBN 协议可以配置出更大的发送窗口 $W_T$,在理论上其所能达到的最大信道利用率会比 SR 协议更高。
  • 术语辨析
    • 滑动窗口协议:特指 GBN 或 SR 协议。
    • 连续 ARQ 协议:也是指 GBN 或 SR 协议。
    • ARQ 协议:是 S-W、GBN、SR 协议的总称。

QQ20260514-092437

介质访问控制(MAC)

1. 介质访问控制(MAC)的概念

  • 问题背景:当多个节点(如A, B, C, D, E)共享同一个“总线型”广播信道时(例如早期的同轴电缆局域网,或现在的 WiFi、5G 等无线通信),如果多个节点同时发送数据,就会发生“信号冲突”。
  • 核心目的:介质访问控制(MAC)就是一套规则,用来控制各个节点对传输介质的访问权限,从而减少甚至避免冲突

2. 频分复用(FDM, Frequency Division Multiplexing)

  • 原理:将共享信道的总频带(总带宽,Hz)在频率维度上划分成多个独立的“子频带”。每个子频带作为一个子信道,分配给一对用户专属使用。
  • 技术细节:发送端通过“复用器”将各个节点发出的信号复合到共享信道上;接收端通过“分用器”将不同子频带的信号分离开来。为了防止子信道之间相互干扰,子频带之间必须留有“隔离频带”。
  • 优缺点
    • 优点:各节点可以同时发送信号,互不干扰,充分利用了信道带宽。
    • 缺点:主要用于模拟信号的传输。

QQ20260514-102253

3. 波分复用(WDM, Wavelength Division Multiplexing)

  • 原理:本质上就是光的频分复用。根据物理公式 $C = \lambda f$(光速 = 波长 × 频率),光的频率与波长成负相关。波分复用就是将不同波长的光信号复合在一起,在同一根光纤中传输。
  • 特点:由于光信号的频带范围(带宽)极其庞大,一根光纤在逻辑上可以被拆分成海量的子信道,极大地提升了传输容量。

4. 时分复用(TDM, Time Division Multiplexing)

  • 原理:将共享信道的传输时间划分为等长的“TDM帧”,每个TDM帧内部再划分为等长的 $m$ 个“时隙”(Time Slot)。将这 $m$ 个时隙固定分配给 $m$ 对用户使用。
  • 特点:用户在分配给自己的时隙内,独占整个信道的频率带宽;但在时间上,各用户是轮流使用信道的。
  • 致命缺点:分配是静态的、固定的。如果某个节点暂不发送数据,分配给它的“时隙”就会一直空着(闲置),导致信道利用率很低。并且每个节点最多只能分到信道总带宽的 $1/m$。

5. 统计时分复用(STDM, Statistic Time Division Multiplexing)

为了解决 TDM 效率低下的问题,引入了 STDM(也称异步时分复用)。

  • 原理:在 TDM 的基础上,不再固定分配时隙,而是根据统计到的各个节点对信道的使用需求,动态按需分配时隙
  • 优点
    • 如果某个节点不发数据,就不会分给它时隙,从而避免了时隙闲置,信道利用率更高
    • 在极端情况下(比如只有节点 A 需要大量发数据),节点 A 可以在一段时间内连续占用所有的时隙,从而瞬间获得所有的信道带宽资源。

QQ20260514-100531

码分复用(Code Division Multiplexing, 简称 CDM)

1. 码分复用(CDM)的核心设计思想

与时分(分时间)、频分(分频率)不同,码分复用允许多个节点在同一时间使用同一频带进行通信,它是通过一种数学编码手段来区分不同信号的。

  • 专属码片序列:网络中的每个节点都会被分配一个独一无二的“码片序列”,通常包含 $m$ 个码片。这可以看作是一个 $m$ 维向量(分量通常取 $1$ 或 $-1$)。
  • 正交要求(核心前提):为了让信号混合后还能被成功分离,分配给各节点的 $m$ 维向量必须相互正交。即任意两个不同节点的向量进行内积计算,结果必须为 $0$(如 $\vec{a} \cdot \vec{b} = 0$)。

2. 发送方如何发送数据?

当节点需要发送二进制比特流时,遵循以下规则:

  • 发送比特 1:节点发出与自身专属“码片序列”完全相同的 $m$ 个信号值(即原向量 $\vec{a}$)。
  • 发送比特 0:节点发出与自身专属“码片序列”完全相反的 $m$ 个信号值(即取反向量 $-\vec{a}$)。

3. 信号的传输与“叠加”

  • 当多个节点(如节点 A 和节点 B)同时向信道发送数据时,它们发出的电磁波信号会在物理信道中产生叠加。
  • 在数学上,这种物理叠加本质上就是多个 $m$ 维向量的线性加法(如 $\vec{a} + \vec{b}$ 或 $\vec{a} + (-\vec{b})$)。

4. 接收方如何“分离”并接收数据?

接收方收到的是混合后的“叠加信号”。由于接收方知道网络中所有节点的专属码片序列,它可以利用正交特性进行数学分离:

  • 规格化内积:将收到的“叠加信号”与目标发送方的“码片序列”进行规格化内积运算(即向量点乘后除以维数 $m$)。
  • 数学消除:由于不同节点的向量相互正交,在计算内积时,其他节点的干扰信号会被消去(乘积为 $0$),只留下目标节点的信号。
  • 解码判定结果
    • 如果规格化内积结果为 $1$,表示该发送方发送了比特 1
    • 如果规格化内积结果为 $-1$,表示该发送方发送了比特 0
    • 如果结果为 $0$,表示该发送方在此刻没有发送数据

QQ20260514-104017

随机访问介质访问控制

image-20260518164351616

上半层:LLC(逻辑链路控制)—— 转运中心的“调度文员”

  • 位置: 紧贴着网络层(第 3 层)。
  • 核心职责:
    • 识别与复用(Multiplexing): 拆开从下层送上来的包裹,看看里面的数据到底是要交给 IP 协议、ARP 协议,还是其他网络层协议。(这就是它“逻辑”的体现,它负责软件层面的对接)。
    • 提供服务接口: 根据需求提供无确认的无连接服务、面向连接的服务等(虽然在现代以太网中,这部分功能被弱化了)。
  • 特点: 它完全是个纯软件逻辑概念,与具体的物理传输介质(光纤、铜线、空气)毫无关系。

下半层:MAC(介质访问控制)—— 转运中心的“车队队长”

  • 位置: 紧贴着物理层(第 1 层)。
  • 核心职责:
    • 组装包裹(成帧): 给 LLC 交下来的数据加上“源 MAC 地址”和“目的 MAC 地址”(我们在局域网里认的就是这个硬件地址),并在末尾加上我们前面算过的 CRC 校验码(FCS)
    • 交通指挥(介质访问): 决定这一秒钟,网卡到底能不能往物理线路上发脉冲信号(也就是我们前面讨论的 CSMA 机制)。
  • 特点: 它和物理硬件深度绑定。你换一块 Wi-Fi 网卡和插一根网线,用的就是完全不同的 MAC 协议。

QQ20260514-175134

1. ALOHA 协议家族

这是最基础的随机访问协议,核心特点是“想发就发”,缺乏对信道状态的探测。

  • 纯 ALOHA (Pure ALOHA)
    • 规则:节点只要准备好数据帧,就立刻发送到信道上。
    • 冲突处理:如果发送后超时未收到确认(ACK),说明发生了冲突。节点会随机等待一段时间后再次尝试重传。
    • 缺点:由于完全不关心信道是否被占用,多节点发送时极易发生帧在时间上的重叠(冲突),信道利用率极低。
  • 时隙 ALOHA (Slotted ALOHA)
    • 规则:将时间划分为等长的“时隙”(大小等于传输一个最大帧的时间)。节点只能在每个时隙的开始时刻才能发送数据。
    • 优点:通过强制时间同步,避免了用户发送数据的随意性。帧要么完全不冲突,要么完全重叠冲突,消除了一半的冲突情况,相比纯 ALOHA 降低了冲突概率,提高了信道利用率。

image-20260514112359990

QQ20260514-105359

2. CSMA 协议(载波监听多路访问)

CSMA 的英文全称是 Carrier Sense Multiple Access,中文通常翻译为载波监听多路访问

为了弥补 ALOHA 协议“不听就发”的缺陷,引入了 CSMA 协议。

  • 核心改进(先听后发):在发送数据之前,必须先监听信道是否空闲。只有在信道空闲时,才会尝试发送。

根据监听策略和发送时机的不同,CSMA 衍生出三种不同的具体协议:

① 1-坚持 CSMA (1-Persistent CSMA)

  • 规则
    • 如果信道空闲,立刻发送数据(发送概率为 1)。
    • 如果信道忙碌,节点会“坚持”监听信道,直到信道变得空闲,然后立刻发送。
  • 优缺点:信道利用率高(一旦空闲马上被利用)。但冲突概率大:如果多个节点都在等待信道空闲,一旦信道释放,它们会同时发送数据,必然导致冲突。

image-20260514112458260

② 非坚持 CSMA (Non-Persistent CSMA)

  • 规则
    • 如果信道空闲,立刻发送数据。
    • 如果信道忙碌,节点放弃监听信道,随机推迟一段时间后,再重新尝试监听。
  • 优缺点:节点通过随机推迟“错开”了发送时间,大大降低了冲突概率。但由于节点盲目等待,可能信道已经空闲了节点还在等待,导致信道利用率降低

image-20260514112514738

③ p-坚持 CSMA (p-Persistent CSMA)

  • 规则
    • 如果信道忙碌,节点“坚持”监听。
    • 如果信道空闲,节点以概率 p 立刻发送数据,以概率 1-p 推迟一段特定的时间(通常是一个端到端传播时延)再重新监听。
  • 优缺点:这是 1-坚持和非坚持方案的折中。既保留了“坚持监听”以提高利用率的优点,又通过“概率发送”降低了多个节点同时发送导致的冲突概率。

image-20260514112524276

QQ20260514-110951

CSMA/CD 协议(带冲突检测的载波监听多路访问)

QQ20260514-171125

1. CSMA/CD 协议的核心口诀

  • 先听后发:发送数据前先监听信道是否空闲(基于 1-坚持 CSMA,即若忙碌则坚持监听,一旦空闲立即发送)。
  • 边听边发:在发送数据的同时,持续监听信道,检测发出的信号是否与其他节点的信号发生碰撞。
  • 冲突停发:一旦检测到冲突,立即停止发送,以节省信道资源。
  • 随机重发:发生冲突后,根据特定的算法随机等待一段时间再重新尝试发送。

QQ20260514-171758

2. “争用期”与无冲突确认

  • 定义:争用期是指一个节点发出数据后,最多需要多久才能确信自己成功“占领地盘”(即确认没有发生冲突)。
  • 计算公式:$\text{争用期} = 2 \times \text{最大单向传播时延}$。这考虑了最极端的情况:两端距离最远的节点 A 和 B,A 发出的信号刚要到达 B 时,B 也开始发送,冲突信号传回 A 刚好需要两倍的传播时延。
  • 重要结论:CSMA/CD 协议没有 ACK 机制。如果节点在经过一个“争用期”后依然没有检测到冲突,就可以断定这次发送一定成功,后续就不可能再发生冲突了。

QQ20260514-173505

3. “最短帧长”的限制

这是为了保证“边听边发”机制有效而设立的硬性规定。

  • 原因:节点只有在发送数据的过程中才能检测冲突。如果帧太短,节点很快就发完了,但此时冲突信号还在路上(还没传回节点)。当冲突信号到达时,节点已经发完了,它会“误以为”发送成功,从而导致错误。

  • 计算公式

    (注:以太网规定最短帧长为 64B 即 512bit)

  • 处理规则:如果实际要发的数据很少,必须“填充”无用数据至合法长度后再发送。接收方若收到小于最短帧长的数据,会直接视为因冲突而中断的无效帧(非法帧)。

QQ20260514-174654

4. 冲突后的退避:截断二进制指数退避算法

当检测到冲突后,节点需要决定等待多久再重发,这里采用的是动态调整等待范围的算法:

  • 退避时间 = $r \times \text{争用期}$,其中 $r$ 是从指定区间内随机抽取的一个整数。
  • 区间计算逻辑:设冲突次数为 $k$。
    1. 当 $k \le 10$ 时,从 $[0, 2^k - 1]$ 区间内随机取整数 $r$。随着冲突次数增加,区间成指数级扩大,意味着节点可能等待的时间越来越长,以此来疏散拥挤的信道。
    2. 当 $10 < k \le 15$ 时,区间不再扩大,固定为 $[0, 2^{10} - 1]$(即 $[0, 1023]$)。
    3. 分水岭:当第 16次冲突发生时,协议会直接“躺平”,放弃传输该帧,并向上级网络层报告错误。

CSMA/CA 协议(带冲突避免的载波监听多路访问)

1. 为什么 WiFi 不用 CSMA/CD?

课件首先对比了有线网的 CSMA/CD 和无线网的 CSMA/CA,解释了无线网络不能使用“冲突检测(CD)”的两个致命原因:

  • 硬件实现极其困难:在无线环境中,设备自身发出的信号强度往往远大于接收到的微弱信号(因为信号在空气中衰减极快)。“边发边听”会导致自己的声音掩盖了别人的声音,根本无法有效检测出冲突。
  • “隐藏站”问题:以太网的碰撞检测是假定了所有的站点都能够听到其他站点是否在发送数据。但在无线局域网的工作环境中,这个假定是不能成立的。在无线通信中,并非所有节点都能互相听见。比如节点 A 和 C 都离中心热点(AP)很近,但 A 和 C 彼此距离很远(超出了信号范围)。此时 A 监听信道会觉得“空闲”,C 也觉得“空闲”,如果它们同时向 AP 发送数据,就会在 AP 处发生严重的冲突。对 A 而言,C 就是一个看不见的“隐藏站”。

QQ20260514-180412

2. CSMA/CA 的核心要点(先发制人,尽量避免)

既然无法在发送中检测冲突,CSMA/CA 的思路就是在发送前想尽一切办法避免冲突

  • 先听后发
    • 若信道空闲,必须再等待一个DIFS(分布式协调功能帧间间隔)的时间后,才能发送帧。一旦开始发送,就一口气发完,中途不检测冲突。
    • 若信道忙碌,则强制进入“随机退避”状态。
  • “随机退避”原理
    • 使用二进制指数退避算法设定一个随机倒计时。
    • 冻结机制:节点在倒计时期间必须保持监听。只有当信道“听起来”空闲时,倒计时才扣减;一旦信道忙碌,倒计时立即冻结。当倒计时归零时,信道必然是空闲的,节点立即发送数据。
  • 确认机制(ACK):因为无法检测冲突,发送方必须依靠接收方返回的 ACK 帧 来确认是否发送成功。如果超时未收到 ACK,发送方只能认为发生了冲突,再次进入“随机退避”并重传(类似于停止-等待协议)。

3. 信道预约机制(解决“隐藏站”的杀手锏)

为了彻底解决隐藏站问题,CSMA/CA 提供了一个可选的信道预约功能(RTS/CTS):

  1. RTS(请求发送):发送方(如节点 A)在发数据前,先向 AP 广播一个极短的控制帧 RTS,里面包含了预计要占用信道的时间。
  2. CTS(允许发送):AP 收到 RTS 后,向四周广播一个 CTS 帧作为回应,里面同样包含预计占用时间。
  3. 虚拟载波监听(禁言机制):关键在于这一步。周围所有的节点(包括 A 的隐藏站 C)一旦收到了 AP 发出的 CTS 帧,就会自动读取里面的时长信息,并设置一个内部计时器(NAV),在此期间自觉保持“禁言”,绝对不发送任何数据。
  4. 安全发送:节点 A 收到 CTS 后,就可以安心地发送冗长的数据帧了,因为此时整个网络都已被 AP “清场”。

4. 帧间间隔(IFS, InterFrame Space)

为了区分不同类型帧的优先级,协议规定了三种长度的等待时间:

  • SIFS(最短):留给接收方处理数据并立即回复(如发送 ACK 或 CTS)的时间,优先级最高。
  • PIFS(中等):用于 PCF 协调功能(考研通常不要求深入)。
  • DIFS(最长):普通的“帧事务”开始前必须等待的时间,优先级最低,为了让高优先级的控制帧先行。

QQ20260514-175254

QQ20260515-095412

QQ20260515-095750

局域网的基本概念和体系结构

QQ20260515-085544

1. 局域网(LAN)的分类与特性

  • 三大特性:覆盖较小的地理范围;具有较低的时延和误码率;局域网内的各节点之间以“帧”为单位进行传输。
  • 传输方式:支持单播(1对1,如A发给B)、广播(1对全体,如A发给所有人)、多播/组播(1对部分特定节点,如A发给B,D,E)。
  • 两大分类
    • 有线局域网:如以太网(IEEE 802.3)、早期已被淘汰的令牌环网。
    • 无线局域网:如 WiFi(IEEE 802.11),采用星形拓扑(1个AP+多台移动设备),使用 CSMA/CA 协议。

2. 以太网(Ethernet)的发展演进

课件详细梳理了以太网传输介质和物理拓扑的演进路线:

  • 早期同轴电缆以太网(10Base5):物理上和逻辑上都是总线形,采用 CSMA/CD 协议争抢信道。
  • 双绞线以太网(10BaseT等)
    • 使用集线器(Hub):物理上是星形,但逻辑上依然是总线形,工作在半双工模式,必须使用 CSMA/CD 协议。
    • 使用交换机(Switch):物理和逻辑上都是星形。交换机支持全双工通信,此时各节点不再需要争抢信道,不需要使用 CSMA/CD 协议(这是一个重要考点)。
  • 光纤以太网(10BaseF等):主要用于扩大局域网覆盖范围。如果是点对点连接且用两条光纤实现全双工通信,同样不需要使用 CSMA/CD。

3. 网络适配器(网卡 / NIC)的核心工作原理

网卡是连接计算机主机与外部局域网的桥梁(硬件架构包含以太网适配器和WiFi适配器)。

  • MAC 地址(物理地址):全球唯一,长 48 bit
    • 高 24 bit 由 IEEE 统一分配给不同的网卡制造厂商。
    • 低 24 bit 由厂商自行分配给生产出的每一块网卡。
    • MAC 地址固化在网卡的 ROM(只读存储器)中。
  • 网卡的五大核心要点(功能)
    1. 封装与解封装:负责把网络层交下来的 IP 数据报封装成“帧”发送到局域网;或从局域网接收“帧”并提取出 IP 数据报。
    2. 中断机制与差错处理:从局域网接收信号,如果收到正确的帧,就使用“中断”通知 CPU 提取数据;如果发现是差错帧,网卡会直接将其静默丢弃,不打扰 CPU。
    3. 协议实现:网卡内部依靠硬件实现了数据链路层物理层的标准功能。
    4. 串 / 并行转换:网卡与主板(CPU/内存)之间通过 I/O 总线进行并行通信,而与外部局域网之间进行串行通信,网卡负责这两种数据流格式的转换。
    5. 帧缓冲:网卡内部带有 RAM(随机存取存储器),用于在发送或接收数据时对“帧”进行缓存。

以太网(Ethernet)的物理层标准、MAC 帧格式

1. 以太网标准与双工模式

不同传输介质对通信模式(半双工/全双工)的支持情况不同:

  • 同轴电缆(如 10BASE5、10BASE2):物理特性决定了它们只能工作在半双工模式。
  • 双绞线(如 10BASE-T):
    • 做题时的默认潜规则:如果连接的是集线器(Hub),只能是半双工(必须使用 CSMA/CD 协议防冲突)。
    • 如果连接的是交换机(Switch),默认支持全双工(各走各的道,不需要使用 CSMA/CD 协议)。
  • 光纤(如 10BASE-F):只支持全双工
  • 高速以太网演进:当以太网速率达到 10Gbps(万兆)时,规定只工作在全双工模式,这也标志着 CSMA/CD 协议在现代高速网络中已经彻底退出历史舞台。

QQ20260515-085819

2. V2 版以太网 MAC 帧格式(重点记忆)

课件给出了一个非常实用的结构记忆口诀:“6 6 2 N 4,收发协议数验”

  • 目的地址(6字节):接收方的 MAC 地址。如果是广播帧,该地址的 48 bit 全为 1(即 FF-FF-FF-FF-FF-FF)。
  • 源地址(6字节):发送方的 MAC 地址。
  • 类型(2字节):指明封装在内部的网络层使用的是什么协议(如 IPv4 或 IPv6)。
  • 数据(N = 46~1500字节):即网络层交下来的 IP 数据报。如果太短必须“填充”凑够 46 字节,太长则在网络层就要“分片”。
  • FCS 校验码(4字节):使用之前学过的 CRC(循环冗余校验)来检测位错。
  • 易错细节:为了让接收方对齐时钟节奏,物理层会在这个 MAC 帧前面强行插入 8 字节的“前导码”(7B 前同步码 + 1B 帧开始定界符)。但这 8 字节不算作 MAC 帧本身的长度。MAC 帧的总长度被严格限制在 64B ~ 1518B 之间。

QQ20260515-090348

3. MAC 地址的归属与设备分级

  • 核心理念:MAC 地址是数据链路层(L2)的概念。
  • 结论:工作在网络层的路由器、工作在链路层的交换机内部都有数据链路层的功能,因此它们都有 MAC 地址。而工作在物理层(L1)的集线器(Hub)\只负责无脑放大电信号,它*没有 MAC 地址*

4. 冲突域与广播域的隔离(必考核心)

这是构建和分析网络拓扑时最关键的原则,决定了广播风暴和冲突的范围:

  • 集线器(Hub):最底层的物理层设备。不隔离冲突域,也不隔离广播域。连在上面的所有电脑“一荣俱荣,一损俱损”,只要有两人同时发数据就冲突,一人发广播所有人必听。
  • 交换机(Switch):数据链路层设备。隔离冲突域,但不隔离广播域。它的每个端口就是一个独立的冲突域(所以连在不同端口的设备可以同时发数据不冲突),但如果有人发广播帧,交换机依然会复制并群发给所有端口。
  • 路由器(Router):网络层设备。既隔离冲突域,也隔离广播域。路由器是广播帧的“终结者”。当路由器收到目的地址全为 1 的广播 MAC 帧时,它会直接拦下丢弃,绝不会将其转发到其他网络段。

QQ20260515-091828

VLAN(虚拟局域网)

1. VLAN 的引入与核心概念

  • 面临的问题:如果一个局域网非常大(连接了大量主机和交换机),整个网络就是一个巨大的“广播域”。一旦有设备发送广播帧,所有设备都会收到,容易引发“广播风暴”,浪费网络资源,且不利于信息安全。
  • VLAN 的作用:在逻辑上将一个大型的物理局域网,分割成若干个较小的虚拟局域网(VLAN)。
  • 核心准则:一个 VLAN 就是一个独立的广播域。 同一个 VLAN 内的主机可以直接通信,不同 VLAN 之间的主机即使连在同一个交换机上,也不能直接进行二层(MAC层)通信,从而隔离了广播。
  • 标识:每个 VLAN 都有一个专属的编号,称为 VID (VLAN ID)

2. 划分 VLAN 的三种方式

交换机内部会维护一张映射表,将数据帧归入不同的 VLAN 中:

  • ① 基于接口:最简单常用的方式。交换机直接记录“端口号”与“VID”的映射关系(例如:插在 1、2 号口的电脑属于 VLAN 10)。
  • ② 基于 MAC 地址:记录“主机的 MAC 地址”与“VID”的映射关系。优点是即使用户把电脑拔下来换插到交换机的另一个端口,它依然属于原来的 VLAN。
  • ③ 基于 IP 地址:记录“网络层 IP 地址”与“VID”的映射关系。这种方式甚至允许 VLAN 的范围跨越路由器。

3. IEEE 802.1Q 帧(重点考点)

为了让跨越多个交换机的同一 VLAN 主机能够通信,交换机之间通过“干线链路(Trunk 链路)”相连。干线链路需要传输多个不同 VLAN 的数据,因此必须对数据帧进行“打标签”以示区分。

  • 传输规则
    • 主机与交换机之间:传输的是无标签的标准以太网 MAC 帧(即之前背的口诀:6 6 2 N 4)。
    • 交换机与交换机之间(干线链路):传输的是打了标签的 802.1Q 帧。交换机在发往另一台交换机前会“插入”标签,另一台交换机收到后,在发给目标主机前会“剥除”标签。
  • 802.1Q 帧的结构演变
    • 在标准 MAC 帧的“源地址(6B)”“类型(2B)”之间,强行插入了 4 字节的 VLAN 标签
    • 新口诀6 6 4 2 N 4,收、发、V(VLAN标签)、协、数、验。
  • 4 字节 VLAN 标签的内部构造
    • 前 2 个字节:固定为 0x8100,交换机一看到这个数字就知道这是一个打了标签的 802.1Q 帧。
    • 后 2 个字节:包含 4 位的无用信息和 12 位的 VID。这 12 位的 VID 明确指出了该帧属于哪一个具体的 VLAN。

QQ20260515-102011

QQ20260515-102025

IEEE 802.11 无线局域网

一、 802.11 无线局域网基本概念

802.11 无线局域网采用星形拓扑结构,其中心节点称为接入点(AP),也可称为无线接入点(WAP)。

  • 基本服务集 (BSS): 由 1 个基站(AP)和 N 个移动站组成。
    • 通俗理解:1 个 WiFi 热点连了很多台手机或电脑。
    • SSID (服务集标识符): 无线局域网的名字(WiFi名称),最长不超过 32 字节。
    • BSA (基本服务区): 一个基本服务集能够覆盖的地理范围(即站在哪里能搜到这个 WiFi)。
  • 扩展服务集 (ESS): 将多个 AP 连接到同一个分配系统(DS),组成一个更大的服务集。
    • 通俗理解:想象成家里的“全屋 WiFi”系统。
  • 漫游 (Roaming): 移动站从一个基本服务集切换到另一个基本服务集,且保持通信连续不中断。
    • 通俗理解:拿着手机走动时,丝滑切换不同的 WiFi 热点。
  • 门户 (Portal): 充当“网桥”的角色,可将 802.11 无线局域网接入 802.3 有线以太网,将两类局域网连接成更大的局域网。

QQ20260515-103344

二、 硬件架构与通信原理

  • 家用路由器架构: 一个普通的家用路由器实际上是三者的结合体:家用路由器 = 路由器 + 以太网交换机 + AP
  • 通信限制: 在 802.11 无线局域网内,两个移动站之间不能直接通信,必须通过基站(AP)进行转发。
  • 链路区别: AP 与移动站之间通过无线链路传输;AP 与 AP、AP 与路由器、AP 与交换机之间通常使用有线链路
  • 格式转换: AP 通常具备“帧格式转换”功能,能在无线链路的 802.11 帧与有线链路的以太网帧之间互相转换。
  • 介质访问控制: 802.11 无线局域网使用 CSMA/CA 协议来实现介质访问控制(包含 RTS 和 CTS 帧的碰撞避免机制)。

三、 802.11 帧的分类

802.11 帧主要分为以下三种类型:

  1. 数据帧: 承载实际传输的数据。
  2. 控制帧: 用于辅助数据传输,如 ACK(确认)、RTS(请求发送)、CTS(允许发送)。
  3. 管理帧: 用于管理无线网络连接,如探测请求/探测响应帧。
    • 通俗理解:主要用于“发现 WiFi”。

四、 802.11 数据帧格式与地址解析

这部分是重难点,尤其涉及 MAC 首部中的地址字段(重点关注地址 1、2、3)以及帧控制位中的“去往 AP”和“来自 AP”的标志位。

核心记忆口诀:

  • 10 表示“去往 AP”(移动站发往 AP):
    • 口诀:去往 AP 中起止
    • 地址 1:转(接收方,即 AP 的 MAC 地址)
    • 地址 2:点(发送方,即源移动站 A 的 MAC 地址)
    • 地址 3:点(最终目的地,即目标移动站 B 的 MAC 地址)
  • 01 表示“来自 AP”(AP 发往移动站):
    • 口诀:来自 AP 止中起
    • 地址 1:点(接收方,即目标移动站 B 的 MAC 地址)
    • 地址 2:转(发送方,即 AP 的 MAC 地址)
    • 地址 3:点(最初源头,即源移动站 A 的 MAC 地址)

(注:地址 4 仅在固定基站的网络互联中使用,一般情况下较少关注。)

QQ20260515-104739

QQ20260515-104952

广域网与ppp协议

一、 广域网 (WAN) 基本概念

  • 特点与任务: 覆盖范围广(几十到几千公里),主要任务是长距离运送主机发送的数据,并将分布在各地的局域网互联起来。
  • 硬件构成: 局域网通过路由器连接到广域网。广域网内部由节点交换机(可理解为广域网专用的专业路由器)通过链路连接而成。
  • 核心对比(重要考点):
    • 广域网 (WAN): 各节点之间通常使用点对点高速链路,数据链路层常使用 PPP 协议
    • 局域网 (LAN): 传统以太网常使用总线型链路,数据链路层常使用 CSMA/CD 协议

二、 PPP 协议核心拆解

PPP(点对点协议)是广域网链路层最常用的协议,主要包含三大组成部分及其相关机制:

1. 三个组成部分

  • LCP (链路控制协议): 用于建立、配置和测试数据链路连接(例如:协商 MTU 大小、身份认证协议)。
  • NCP (网络控制协议): 为网络层协议建立和配置逻辑连接。每个不同的网络层协议需要相应的 NCP(例如:为 IP 协议分配 IP 地址的叫 IPCP)。
  • PPP 帧格式: 定义了将网络层数据(如 IP 数据报)封装成帧的格式。

2. PPP 帧格式详解

  • 标志字段 (F): 首尾各有一个,固定为 0x7E (01111110),作为帧定界符。
  • 地址 (A) 与控制 (C): 固定数值,无实际意义。
  • 协议: 2字节,用于指明“信息部分”携带的是什么协议的数据(如:IP 数据报、LCP 数据或 NCP 数据)。
  • 信息部分: 实际携带的有效载荷,不超过 1500 字节。
  • FCS (帧检验序列): 2字节,采用 CRC 循环冗余校验,若出错则直接丢弃该帧。

3. 透明传输机制

为了防止信息部分出现与帧定界符 0x7E 相同的比特组合导致误判,PPP 采用以下两种填充方式:

  • 字符填充(用于异步传输): 逐个字符传送。以 0x7D 作为转义字符来替换数据中出现的 0x7E 等特殊控制字符。
  • 零比特填充(用于同步传输): 连续比特流传送。发送端在发现连续 5 个 1 时,自动填入一个 0(接收端反向删除),确保数据中不会出现 01111110

4. PPP 协议的主要特点

  • 仅支持点对点全双工链路。
  • 不可靠: 只保证无差错(通过 CRC 检错丢弃),但不提供确认(没有 ACK 机制)。
  • 支持多种网络层协议(如 IPv4、IPv6)。
  • 面向字节,帧长度是字节的整数倍。

QQ20260515-111957


三、 PPP 协议工作过程(以早期拨号上网为例)

当你使用老电脑连接 ISP(网络服务提供商)的接入服务器时,PPP 的工作状态流转如下:

  1. 物理链路建立: 硬件设备连接,建立底层的物理层连接。
  2. LCP 链路建立: 双方通过发送 LCP 请求/应答帧,协商网络参数(如 MTU、认证方式)。
  3. LCP 链路鉴别: 用户发送账号密码进行身份验证(认证成功或失败)。
  4. NCP 链路建立: 鉴别通过后,发送 NCP 请求,ISP 服务器为你分配一个 IP 地址。
  5. 网络连通: LCP 和 NCP 链路均建立完毕,开始使用 PPP 帧携带 IP 数据报愉快地上网。

补充延伸: 随着技术发展,“拨号上网”时代过去后,为了在现代以太网中使用 PPP 协议的认证等优秀特性,演变出了 PPPoE 协议(PPP over Ethernet),其本质就是在以太网 MAC 帧的数据部分套娃携带了 PPP 帧

以太网交换机

QQ20260516-100605

一、 以太网交换机的基本特点

  • 本质: 交换机相当于一个多端口网桥(支持全双工通信)。
  • 工作层次: 工作在数据链路层
  • 转发依据: 交换机根据数据帧的目的 MAC 地址来决定如何转发数据帧。

二、 核心机制:自学习功能(支持即插即用)

交换机内部维护着一张交换表,记录了【MAC 地址】与【端口号】的对应关系。该表初始为空,通过“自学习”不断完善:

  1. 学习(记录“从哪来”):
    • 每当交换机收到一个帧,就会读取该帧的源 MAC 地址(发送方),并将其与接收该帧的端口号记录/更新到交换表中。
  2. 转发(决定“往哪去”):
    • 情况 A(未知目标): 如果交换表中找不到“目的 MAC 地址”(接收方)对应的端口,交换机会将该帧广播到除接收端口之外的所有其他端口。
    • 情况 B(已知目标): 如果交换表中存在“目的 MAC 地址”的对应端口,交换机会精准地将帧转发到该端口(单播)。
  3. 老化机制(应对网络拓扑变化):
    • 思考题指出:如果节点物理位置发生移动(例如拔掉网线换到另一个端口),旧的记录会导致转发错误。
    • 解决方案: 交换表中的每个表项都设有“有效时间”。如果一段时间内没有收到来自该 MAC 地址的帧,该表项就会过期自动作废,从而保证交换机能学习到节点最新的位置。

QQ20260516-100427

三、 两种数据交换方式对比

在决定从哪个端口转发之前,交换机处理数据帧的具体方式分为两种(结合 V2 标准以太网 MAC 帧格式:6 6 2 N 4,即目的地址、源地址、类型、数据、FCS校验):

  • 1. 直通交换 (Cut-through)
    • 工作原理: 交换机接收到帧时,只读取前 6 个字节(目的 MAC 地址),一旦查表确定了输出端口,就立刻开始转发。
    • 优点: 转发时延非常低。
    • 缺点: 无法进行差错检测,也不适用于需要速率匹配或协议转换的线路。
  • 2. 存储转发交换 (Store-and-forward)
    • 工作原理: 交换机会先将整个数据帧完整地接收并放入内部的高速缓存中。接着进行 FCS 差错检测等必要处理。如果帧正确,再根据交换表将其转发出去;如果出错,则直接丢弃。
    • 优点: 能够检测出错误帧并丢弃,同时适用于需要速率匹配、协议转换或差错检测的线路。
    • 缺点: 转发时延较高。

QQ20260516-100952

SSH 是什么

SSH 的全称是 Secure Shell,中文一般翻译成“安全外壳协议”。

它最常见的用途是:让我们在本地电脑上,安全地登录到另一台远程服务器,并在远程服务器上执行命令。

比如:

1
ssh user@example.com

这条命令的意思是:用 user 这个用户名,通过 SSH 登录到 example.com 这台服务器。

SSH 解决的核心问题是:在不可信的网络里,安全地连接另一台机器

它通常会做几件事:

  • 身份认证:确认你是不是有权限登录这台服务器,比如密码登录、密钥登录。
  • 加密通信:登录后输入的命令、返回的结果都会被加密传输。
  • 远程命令执行:可以像操作本地终端一样操作远程服务器。
  • 端口转发:也就是常说的 SSH 隧道,可以把某个网络连接“套”进 SSH 加密连接里传输。

这篇笔记主要记录 SSH 的 端口转发 / 隧道 用法。

SSH 隧道是什么

SSH 隧道的本质是:借用一条已经建立好的 SSH 连接,把其他 TCP 流量转发过去

可以先把 SSH 想成一条安全管道:

1
本地电脑  <====== 加密 SSH 连接 ======>  SSH 服务器

正常情况下,这条管道只用来传输终端命令。

但端口转发会让这条管道承担更多事情:把某个端口收到的流量,经由 SSH 连接送到另一端,再转发给真正的目标服务。

常见的 SSH 隧道有三种:

类型 参数 核心方向 记忆方式
本地隧道 -L 本地端口 -> SSH 服务器侧的目标服务 把远程服务拉到本地
远程隧道 -R 远程端口 -> 本地侧的目标服务 把本地服务暴露到远程
动态隧道 -D 本地 SOCKS 代理 -> SSH 服务器 -> 任意目标 让 SSH 临时变成代理

本地隧道:把远程服务拉到本地

一句话理解:在本地开一个端口,访问这个本地端口时,流量会通过 SSH 转发到远程网络里的某个服务。

命令模板:

1
ssh -L 本地监听端口:目标主机:目标端口 用户名@SSH服务器

例子:

1
ssh -L 3307:127.0.0.1:3306 user@example.com

这条命令的含义是:

  • 在本地电脑监听 3307 端口。
  • 通过 SSH 登录到 example.com
  • 当本地访问 127.0.0.1:3307 时,流量会被送到 SSH 服务器那一侧的 127.0.0.1:3306

如果远程服务器上有一个 MySQL 只监听自己的 127.0.0.1:3306,外部不能直接访问,那么本地隧道就可以让你在本地这样连:

1
mysql -h 127.0.0.1 -P 3307 -u root -p

注意:这里连的是本地的 3307,但真正到达的是远程服务器那边的 3306

本地隧道示意图

适用场景

  • 远程数据库不允许公网访问,只允许服务器本机访问。
  • 公司内网服务只能从跳板机访问。
  • 想安全地临时访问远程机器上的管理后台。

容易混淆的点

目标主机 是站在 SSH 服务器那一侧 来看的。

比如:

1
ssh -L 8080:127.0.0.1:80 user@example.com

这里的 127.0.0.1:80 不是你本地电脑的 80 端口,而是 example.com 那台服务器自己看到的 127.0.0.1:80

远程隧道:把本地服务暴露到远程

一句话理解:在远程服务器上开一个端口,别人访问远程服务器这个端口时,流量会通过 SSH 反向回到本地服务。

命令模板:

1
ssh -R 远程监听端口:目标主机:目标端口 用户名@SSH服务器

例子:

1
ssh -R 9000:127.0.0.1:3000 user@example.com

这条命令的含义是:

  • 通过 SSH 登录到 example.com
  • 在远程服务器上监听 9000 端口。
  • 当远程服务器访问自己的 127.0.0.1:9000 时,流量会通过 SSH 隧道回到本地电脑的 127.0.0.1:3000

如果你本地正在跑一个 Web 服务:

1
npm run dev

服务地址是:

1
http://127.0.0.1:3000

那么远程隧道可以让 SSH 服务器通过下面这个地址访问到它:

1
http://127.0.0.1:9000

远程隧道示意图

适用场景

  • 本地电脑没有公网 IP,但想让远程服务器访问本地服务。
  • 在服务器上借用本地电脑的代理端口。
  • 临时把本地开发服务暴露给远程环境调试。

容易混淆的点

远程隧道默认通常只监听远程服务器自己的 127.0.0.1

也就是说,下面这条命令:

1
ssh -R 9000:127.0.0.1:3000 user@example.com

一般只代表 SSH 服务器本机 可以访问 127.0.0.1:9000,不一定代表互联网上所有人都能访问 example.com:9000

如果希望远程端口监听公网地址,通常还涉及 SSH 服务端配置,比如 GatewayPorts,这个属于更高风险的公网暴露场景,需要谨慎处理。

动态隧道:让 SSH 变成 SOCKS 代理

一句话理解:在本地开一个 SOCKS 代理端口,应用把请求交给这个代理后,SSH 会把请求转发到不同的目标地址。

命令模板:

1
ssh -D 本地SOCKS端口 用户名@SSH服务器

例子:

1
ssh -D 1080 user@example.com

这条命令的含义是:

  • 在本地电脑监听 1080 端口。
  • 这个端口不是固定转发到某一个服务,而是作为 SOCKS 代理使用。
  • 浏览器或其他程序把请求交给 127.0.0.1:1080 后,SSH 会根据请求里的目标地址,帮你从 SSH 服务器那一侧访问目标网站或服务。

浏览器代理可以这样理解:

1
浏览器 -> 127.0.0.1:1080(SOCKS5) -> SSH 服务器 -> 目标网站

动态隧道示意图

适用场景

  • 想让浏览器临时通过 SSH 服务器访问网络。
  • 目标地址很多,不想为每个服务单独写一个 -L
  • 需要一个临时 SOCKS5 代理。

容易混淆的点

动态隧道和本地隧道都在本地开端口,但区别很大:

  • -L 是固定转发:一个本地端口对应一个固定目标。
  • -D 是动态代理:目标地址由应用的 SOCKS 请求决定。

所以 -D 后面不需要写 目标主机:目标端口

三种隧道对比

对比项 本地隧道 -L 远程隧道 -R 动态隧道 -D
监听端口在哪里 本地电脑 SSH 服务器 本地电脑
流量最终去哪里 SSH 服务器那一侧的固定目标 本地电脑那一侧的固定目标 请求指定的动态目标
是否固定目标 固定 固定 不固定
常见用途 访问远程内网服务 让远程机器访问本地服务 临时 SOCKS 代理
记忆方式 拉回来 推过去 代理出去

更口语化地记:

  • 本地隧道 -L:我在本地开门,把远程服务拉到我面前。
  • 远程隧道 -R:我在远程开门,把访问反向带回本地。
  • 动态隧道 -D:我在本地开一个代理入口,后面去哪由请求自己决定。

参考资料

没问题,这个方法叫 SSH 远程端口转发(SSH Remote Port Forwarding)。它是绕过校园网/公司内网限制、借用本地网络最安全且最高效的“基操”。

以下是为你整理的完整流程手册,你可以直接收藏:


🛠️ 学校服务器“借网”出海手册

第一步:在本地电脑开启隧道

你自己电脑的终端(Windows PowerShell 或 Mac Terminal)中运行,不要在 VS Code 的内置终端里运行。

Bash

1
2
3
4
5
6
7
# 命令格式:ssh -R [远程端口]:127.0.0.1:[本地代理端口] [用户名]@[服务器IP]
ssh -R 7890:127.0.0.1:7890 wjw@223.2.18.229
wjw19230415
ssh -R 7890:127.0.0.1:7890 zxj@223.2.18.229
cd MLLM_pre
npx @anthropic-ai/claude-code

  • -R: 开启远程转发模式。
  • 7890: 这里的两个数字必须对应。前一个是服务器上开启的口子,后一个是你本地梯子的监听端口。

第二步:在服务器终端挂载代理

连接成功进入服务器后,在弹出的黑色窗口中输入以下命令,告诉系统“指路牌”:

1
2
3
export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
export all_proxy=http://127.0.0.1:7890

注意:这几行命令只对当前这个窗口有效。如果你关掉窗口重新连,需要重新输入。


第三步:验证联网状态

输入测试命令,观察是否返回 HTTP/2 200

Bash

1
curl -I https://www.google.com

第四步:启动 Copilot CLI

联网成功后,直接运行你安装好的工具:

Bash

1
npx @github/copilot /login

⚠️ 避坑指南(如果失败请检查这里)

  1. 本地梯子设置

    必须打开梯子软件(Clash/v2ray/Surfboard 等)中的 “允许局域网连接 (Allow LAN)” 开关。即使是在同一台电脑通过隧道回传,软件有时也会拦截非本地请求。

  2. 端口匹配

    如果你的梯子端口不是 7890(比如是 108091080),请把上面所有步骤中的 7890 替换成你的实际端口。

  3. VS Code 侧边栏同步

    如果你想让 VS Code 的 Chat 插件也通,请在 VS Code 的 settings.json 中添加:

    "http.proxy": "http://127.0.0.1:7890"

    由于你已经开了 SSH 隧道,此时远程服务器会认为 127.0.0.1:7890 就是它的出口。


💡 进阶:如何不用每次都打 export

你可以把代理命令写入服务器的个人配置文件中:

  1. 输入 vim ~/.bashrc

  2. 在文件最后添加:

    alias proxyon="export https_proxy=http://127.0.0.1:7890 http_proxy=http://127.0.0.1:7890 all_proxy=http://127.0.0.1:7890"

  3. 保存退出后,下次连上服务器只需输入 proxyon 即可一键开启。

这个流程存好了吗?如果你准备好尝试在服务器上直接安装一个永久“梯子”(不需要本地电脑一直开着),我们可以开始讲解 Tailscale 的方案。

tailscale/tailscale: The easiest, most secure way to use WireGuard and 2FA.

Repository search results

Releases · MetaCubeX/mihomo

SSH隧道的原理和实操 | 本地隧道、远程隧道、动态隧道_哔哩哔哩_bilibili

常用命令

1
2
3
4
5
6
7
8
openclaw gateway restart
openclaw gateway start
openclaw gateway stop

openclaw dashboard
openclaw onboard
openclaw gateway status
openclaw models auth login --provider openai-codex

长期记忆

方式

OpenClaw 记忆是智能体工作空间中的纯 Markdown 文件。这些文件是唯一的事实来源;模型只”记住”写入磁盘的内容。

这里面的记忆又分为两种,分别是用户级的记忆和日志记忆,memory文件夹中存放的是日志记忆,记录每天对话的需要长期存储的内容

新对话长期记忆的读取:1.读取日志记忆中今天和昨天的内容。2.读取用户级记忆

image-20260204150129994

还可以通过配置,设定额外的记忆空间

1
2
3
4
5
6
7
agents: {
defaults: {
memorySearch: {
extraPaths: ["../team-docs", "/srv/shared-notes/overview.md"]
}
}
}

除了对话和用户的记忆,openclaw本身的功能文件也是markdown存储

  1. 本地文档路径C:\Users\ASUS\AppData\Roaming\npm\node_modules\openclaw\docs
  2. 技能(skills)路径:C:\Users\ASUS\AppData\Roaming\npm\node_modules\openclaw\skills\

image-20260204153018870

何时写入记忆

对于何时写入记忆分为两种:

1.当用户谈及决策、偏好和持久性事实,写入 MEMORY.md

2.日常笔记和运行上下文写入 memory/YYYY-MM-DD.md。当会话达到上下文限制时,会触发压缩,将当前会话记忆存储于memory/YYYY-MM-DD.md

用户同样可以通过对话进行记忆的存储

image-20260204151029801

记忆的压缩

当会话接近自动压缩时,OpenClaw 会询问agent是否需要型在上下文被压缩之前写入持久记忆。

这由 agents.defaults.compaction.memoryFlush 控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
agents: {
defaults: {
compaction: {
reserveTokensFloor: 20000,
memoryFlush: {
enabled: true,
softThresholdTokens: 4000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}

reserveTokensFloor: 20000代表必须要在还剩20000tokens前进行压缩(最后底线)

softThresholdTokens: 4000代表一个缓冲空间,当会话 token 估计超过 contextWindow - reserveTokensFloor - softThresholdTokens 时触发压缩提醒

记忆的搜索

openclaw分为两种记忆搜索的方式

1.使用向量索引构建小型的rag,需要配置向量模型的api或本地部署向量模型(默认方式)

2.如果没有配置向量模型,openclaw会选择直接遍历查看markdown文件进行记忆搜索

向量搜索

OpenClaw 可以在 MEMORY.mdmemory/*.md上构建小型向量索引,以便语义查询可以找到相关笔记

流程如下:

  • 默认使用远程嵌入。如果未设置 memorySearch.provider,OpenClaw 自动选择:
    1. 如果配置了 memorySearch.local.modelPath 且文件存在,则使用 local
    2. 如果可以解析 OpenAI 密钥,则使用 openai
    3. 如果可以解析 Gemini 密钥,则使用 gemini
    4. 否则记忆搜索保持禁用状态直到配置完成。
  • 本地模式使用 node-llama-cpp,可能需要运行 pnpm approve-builds
  • 使用 sqlite-vec(如果可用)在 SQLite 中加速向量搜索。

为什么 OpenAI 批处理快速又便宜:

  • 对于大型回填,OpenAI 通常是我们支持的最快选项,因为我们可以在单个批处理作业中提交许多嵌入请求,让 OpenAI 异步处理它们。
  • OpenAI 为 Batch API 工作负载提供折扣定价,因此大型索引运行通常比同步发送相同请求更便宜。
  • 详情参见 OpenAI Batch API 文档和定价:

批处理模式 (Batch):你把几千个、几万个请求打包成一个文件塞给 OpenAI,并告诉它:“我不急,你 24 小时内给我就行。”

原理:OpenAI 会利用服务器的闲置时段(比如凌晨)来处理这些任务。因为你帮它平摊了服务器压力,所以它给你 50% 甚至更多的折扣

记忆工具

openclaw提供了两种记忆工具

memory_searchMEMORY.md + memory/**/*.md 语义搜索 Markdown 块。它返回片段文本、文件路径、行范围、分数,以及我们是否从本地回退到远程嵌入。不返回完整文件内容

memory_get 读取特定的记忆 Markdown 文件(工作空间相对路径),可选从起始行开始读取 N 行。

两个工具仅在智能体的 memorySearch.enabled 解析为 true 时启用。

image-20260204155222237

搜索方式

OpenClaw 结合:

  • 向量相似度(语义匹配,措辞可以不同)
  • BM25 关键词相关性(精确令牌如 ID、环境变量、代码符号)

构建索引

索引存储:每个智能体的 SQLite 位于 ~/.openclaw/memory/<agentId>.sqlite

配置向量模型

image-20260204160814962

查看构建结果

image-20260204161159014

image-20260204161150230

会话记忆

  • Gateway 网关主机上:
    • 存储文件:~/.openclaw/agents/<agentId>/sessions/sessions.json(每个智能体)。
  • 对话记录:~/.openclaw/agents/<agentId>/sessions/<SessionId>.jsonl(Telegram 话题会话使用 .../<SessionId>-topic-<threadId>.jsonl)。

image-20260204162005555

image-20260204161956796

参考资料

openclaw/openclaw: Your own personal AI assistant. Any OS. Any Platform. The lobster way. 🦞

Gateway Runbook - OpenClaw

【Clawdbot为什么能记住你说过的话? AI记忆系统拆解】 https://www.bilibili.com/video/BV1fv61B4EQ5/?share_source=copy_web&vd_source=5e54f7845fd2cf2828efb1bae2286590

TCP和UDP协议的核心原理和区别

TCP:全称是 Transmission Control Protocol,中文翻译为传输控制协议

UDP:全称是 User Datagram Protocol,中文翻译为用户数据报协议

1. 连接类型和服务对象

这决定了通信双方在“开聊”之前要做什么,以及能和谁聊。

  • TCP(面向连接)
    • 连接类型:必须通过“三次握手”建立逻辑连接。通信结束后,还需“四次挥手”断开。
    • 服务对象:它是端到端的,即点对点通信。一旦连接建立,就是两个特定 IP/端口之间的独占通道。
  • UDP(无连接)
    • 连接类型:完全无连接。想发就发,不需要预先建立任何关系。
    • 服务对象:支持一对一、一对多(广播)、多对一和多对多(组播)。它更像是一个大喇叭,谁都能听,谁都能喊。

2. 首部(Header)

image-20260126145037525

这决定了协议的“管理成本”和传输效率。

  • TCP(臃肿但功能全)
    • 长度:最小 20 字节,如果包含选项字段会更长。
    • 内容:包含序列号、确认号、各种标志位(SYN/ACK/FIN)、窗口大小等。这些复杂的字段都是为了确保“可靠传输”。
  • UDP(简洁精悍)
    • 长度:固定为 8 字节
    • 内容:只包含四个字段:源端口、目的端口、长度和校验和。这让它的处理速度极快,开销极低。

3. 可靠性与传输方式

image-20260126145256624

这是两者最本质的区别。

  • TCP(可靠的字节流)
    • 可靠性:通过确认应答(ACK)、超时重传、丢包检测来保证数据不丢失、不重复、且按序到达
    • 传输方式面向字节流。TCP 不管你应用层发的是什么,它把数据看成一串流。它可能会把你的大报文拆开,也可能把几个小报文合并。
  • UDP(不可靠的报文)
    • 可靠性尽力而为。不保证送达,也不保证顺序。丢了就丢了,UDP 本身不负责重传。
    • 传输方式面向报文。你给它一个 500 字节的包,它就原封不动打上头发出去,保留了报文的边界,不会进行拆分或合并。

4. 流量控制和拥塞控制

这决定了协议如何应对网络环境的变化。

  • TCP(智能管家)
    • 流量控制:通过“滑动窗口”机制,根据接收方的处理能力动态调整发送速度,防止把对方“淹没”。
    • 拥塞控制:当网络出现拥堵时,TCP 会通过慢启动、拥塞避免等算法主动降低发送速率。它有很强的自我约束和适应能力
  • UDP(热血青年)
    • 控制机制完全没有。网络堵不堵、对方收不收得下,UDP 全然不理。它只会按应用层要求的速度拼命发。这种特性在网络差时可能导致严重的丢包,但也保证了实时性。

5. 应用场景

基于上述特性,它们的应用领域泾渭分明。

协议 适用核心需求 典型应用
TCP 准确性第一,宁慢勿错 浏览器(HTTP/HTTPS)文件传输(FTP)邮件(SMTP)远程登录(SSH)
UDP 速度/实时性第一,允许少量错误 视频会议(Zoom/WeChat)在线直播实时竞技游戏域名解析(DNS)

TCP三次握手和四次挥手

理解 TCP 的“三次握手”和“四次挥手”,本质上是理解两个终端如何在不可靠的网络环境下,建立并优雅地释放一个可靠的双工(双向)通道。


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

image-20260126145536691

目的是确认双方的收发能力都正常,并交换初始序列号(ISN)。

  1. 第一次握手 (SYN):客户端发送一个 SYN 包(序列号为 $x$)给服务端。
    • 潜台词:客户端说:“我想和你建连,这是我的初始序列号。”
  2. 第二次握手 (SYN + ACK):服务端收到后,回传一个 SYN 包(序列号为 $y$)和 ACK 包(确认号为 $x+1$)。
    • 潜台词:服务端说:“收到!我同意建连,这是我的序列号,我也确认收到你的了。”
  3. 第三次握手 (ACK):客户端再回传一个 ACK 包(确认号为 $y+1$)。
    • 潜台词:客户端说:“收到!那我们正式开始传数据吧。”

为什么要三次?

为了防止“已失效的连接请求报文”突然又传到了服务端。如果只有两次握手,服务端一响应就建立连接,那么旧的重复请求会导致服务端浪费资源去维持一个根本不存在的连接。


2. 四次挥手(释放连接)

image-20260126151050859

由于 TCP 是全双工的,断开连接时需要双方都确认“我没数据要发了”。

  1. 第一次挥手 (FIN):客户端发一个 FIN 包给服务端。
    • 状态:客户端进入 FIN_WAIT_1,表示客户端不再发数据了。
  2. 第二次挥手 (ACK):服务端回一个 ACK
    • 状态:服务端进入 CLOSE_WAIT。此时连接处于半关闭状态,服务端可能还有没发完的数据。
  3. 第三次挥手 (FIN):服务端发完最后的数据,发送 FIN 包给客户端。
    • 状态:服务端进入 LAST_ACK
  4. 第四次挥手 (ACK):客户端收到后,回一个 ACK,然后进入 TIME_WAIT 状态。
    • 状态:经过 $2MSL$(最长报文段寿命)时间后,客户端正式关闭。

参考资料

【神奇的滑动窗口 | TCP流量控制】 https://www.bilibili.com/video/BV1sQXDYREwP/?share_source=copy_web&vd_source=5e54f7845fd2cf2828efb1bae2286590

【“我为人人,人人为我”的TCP拥塞控制】 https://www.bilibili.com/video/BV1jWS7BeEX5/?share_source=copy_web&vd_source=5e54f7845fd2cf2828efb1bae2286590

0%