<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>网络协议 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 19 Dec 2017 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>基于 UDP 通信的洋葱网络设计：服务器、Android 客户端与 P2P 控制总结</title>
      <link>https://ftmao.com/blog/posts/udp-onion-network-and-p2p-design/</link>
      <pubDate>Tue, 19 Dec 2017 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/udp-onion-network-and-p2p-design/</guid>
      <description>&lt;h1 id=&#34;基于-udp-通信的洋葱网络设计服务器android-客户端与-p2p-控制总结&#34;&gt;基于 UDP 通信的洋葱网络设计：服务器、Android 客户端与 P2P 控制总结&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;最近要实现端到端的加密通信，现在把设计方案记录下来。目标读者：刚接触洋葱网络、P2P 或 UDP 信令设计，想了解「怎么用 UDP 搭一个匿名转发网络，再基于它做 P2P」的同学。&lt;br&gt;
阅读目标：建立整体架构图、理解关键模块职责、记住常见踩坑点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面为什么用-udp-做洋葱网络--p2p-信令&#34;&gt;0. 写在前面：为什么用 UDP 做洋葱网络 + P2P 信令？&lt;/h2&gt;
&lt;p&gt;洋葱网络（Onion Network）的核心思想是：&lt;strong&gt;数据像剥洋葱一样，一层一层加密转发&lt;/strong&gt;。每一跳节点只知道上一跳和下一跳，不知道完整路径，从而保护通信双方的隐私。&lt;/p&gt;
&lt;p&gt;传统洋葱路由（如 Tor）多跑在 TCP 上，因为 TCP 可靠、有序。但在移动场景或 P2P 场景里，UDP 有几个独特优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开销小&lt;/strong&gt;：UDP 头只有 8 字节，没有连接建立/拆除开销，适合移动弱网。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT 友好&lt;/strong&gt;：UDP 是 NAT 打洞（Hole Punching）最常用的传输层协议，STUN/TURN/ICE 都优先用 UDP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适合信令&lt;/strong&gt;：P2P 建立连接前的「打招呼」消息很短，UDP 足够用；真正的大数据再切到 P2P 直连或中继。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;抗审查更灵活&lt;/strong&gt;：无连接特性让流量更像普通 DNS/QUIC/游戏报文，便于混淆。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但 UDP 也带来问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不保证可靠、不保证顺序，需要自己在应用层做重传、去重、拥塞控制。&lt;/li&gt;
&lt;li&gt;报文可能被分片、被防火墙限制、被运营商 QoS。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章会带你从「一个 UDP 报文怎么进洋葱网络」开始，逐步拆解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;洋葱网络的层次结构与加密过程。&lt;/li&gt;
&lt;li&gt;服务器（目录服务 + 中继 + 信令服务）应该怎么设计。&lt;/li&gt;
&lt;li&gt;Android 客户端的结构与关键代码思路。&lt;/li&gt;
&lt;li&gt;怎么用这套 UDP 信令网络做 P2P 控制（NAT 穿透、打洞、直连、中继回退）。&lt;/li&gt;
&lt;li&gt;新人最容易踩的坑与规避方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-洋葱网络核心概念先建立知识地图&#34;&gt;1. 洋葱网络核心概念：先建立「知识地图」&lt;/h2&gt;
&lt;h3 id=&#34;11-洋葱路由-vs-普通代理的区别&#34;&gt;1.1 洋葱路由 vs 普通代理的区别&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;对比项&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;普通代理&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;洋葱网络&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;路径&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;单跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;多跳（通常 3 跳）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;加密&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;端到端一次加密&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每跳一层加密&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;节点知情范围&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;代理知道你是谁、你访问哪&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每跳只知道上一跳和下一跳&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;代表&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HTTP/SOCKS 代理&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Tor、I2P、自定义洋葱网络&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;12-一次洋葱转发的过程&#34;&gt;1.2 一次洋葱转发的过程&lt;/h3&gt;
&lt;p&gt;假设 Alice 要给 Bob 发消息，路径为：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt; E[&amp;#34;入口节点 (Entry)&amp;#34;] --&amp;gt; M[&amp;#34;中间节点 (Middle)&amp;#34;] --&amp;gt; X[&amp;#34;出口节点 (Exit)&amp;#34;] --&amp;gt; B[Bob]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;Alice 会先把报文&lt;strong&gt;从后往前&lt;/strong&gt;逐层加密：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用 Exit 的公钥加密「Exit → Bob 的真实数据」，得到 &lt;code&gt;C3&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用 Middle 的公钥加密「下一站是 Exit + C3」，得到 &lt;code&gt;C2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用 Entry 的公钥加密「下一站是 Middle + C2」，得到 &lt;code&gt;C1&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后 Alice 把 &lt;code&gt;C1&lt;/code&gt; 发给 Entry。Entry 用自己的私钥解密后，看到「下一站是 Middle」，把 &lt;code&gt;C2&lt;/code&gt; 转发给 Middle。Middle 解密后转发 &lt;code&gt;C3&lt;/code&gt; 给 Exit。Exit 解密后拿到真实数据，发给 Bob。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt;|C1| E[Entry&amp;lt;br/&amp;gt;解密: 下一站 Middle] --&amp;gt;|C2| M[Middle&amp;lt;br/&amp;gt;解密: 下一站 Exit] --&amp;gt;|C3| X[Exit&amp;lt;br/&amp;gt;解密: 真实数据] --&amp;gt;|明文| B[Bob]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人记忆点&lt;/strong&gt;：洋葱网络的数据是「加密好的俄罗斯套娃」，每到一个节点就剥掉一层外衣。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;13-udp-洋葱报文的基本结构&#34;&gt;1.3 UDP 洋葱报文的基本结构&lt;/h3&gt;
&lt;p&gt;一个 UDP 洋葱报文可以简单设计为：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  版本 (1B)     |  命令 (1B)     |    跳数 (1B)   |   预留 (1B)    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                        会话 ID / 电路 ID (4B)                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         下一跳洋葱层数据 ...                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;版本&lt;/strong&gt;：协议版本，方便后续升级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令&lt;/strong&gt;：路由、数据、建立、销毁、心跳等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳数 / TTL&lt;/strong&gt;：防止报文无限循环，每经过一跳减 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;会话 ID / 电路 ID&lt;/strong&gt;：标识一条逻辑链路，让节点知道该用哪把密钥解密。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;下一跳洋葱层数据&lt;/strong&gt;：被当前节点私钥解密后，才能得到真正的下一跳地址和载荷。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人容易混淆的点&lt;/strong&gt;：洋葱网络里的「加密」不是端到端 TLS，而是&lt;strong&gt;逐跳加密&lt;/strong&gt;。每一跳都能解密自己最外层，但看不到内层。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-系统整体架构&#34;&gt;2. 系统整体架构&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 公网服务层
        DS[Directory Server&amp;lt;br/&amp;gt;目录服务：发布可信节点]
        SS[Signaling Server&amp;lt;br/&amp;gt;信令服务：NAT 信息交换]
        RS[Relay Server&amp;lt;br/&amp;gt;中继服务：UDP 洋葱转发]
    end

    subgraph Android 客户端
        AC[App 业务层]
        CM[Connection Manager&amp;lt;br/&amp;gt;管理 Circuits / Peers]
        OC[Onion Core&amp;lt;br/&amp;gt;分层加密 / 解密]
        NC[Network Core&amp;lt;br/&amp;gt;UDP Socket / NAT / ICE]
    end

    subgraph 对端
        P2P[Peer B&amp;lt;br/&amp;gt;Android / Server / IoT]
    end

    AC --&amp;gt; CM
    CM --&amp;gt; OC
    CM --&amp;gt; NC
    OC --&amp;gt; NC
    NC --&amp;gt;|UDP| RS
    NC --&amp;gt;|UDP| SS
    NC -.-&amp;gt;|查询节点列表| DS
    NC -.-&amp;gt;|P2P 直连或中继| P2P&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;21-三类服务器职责&#34;&gt;2.1 三类服务器职责&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;服务&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;职责&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;协议&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Directory Server（目录服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;维护可信中继节点列表、公钥、负载、版本&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HTTPS / UDP 查询&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Signaling Server（信令服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;帮助两个 Peer 交换公网 IP:Port、NAT 类型、候选地址&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP + 可靠信令协议&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Relay Server（中继服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;当 P2P 打洞失败时，转发 UDP 洋葱流量&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP 洋葱路由&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;实际部署中，目录服务可以用 HTTPS 更稳；信令和中继为了速度和 NAT 友好，优先用 UDP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-服务器端设计&#34;&gt;3. 服务器端设计&lt;/h2&gt;
&lt;h3 id=&#34;31-目录服务directory-server&#34;&gt;3.1 目录服务（Directory Server）&lt;/h3&gt;
&lt;p&gt;目录服务就像「黄页」，让客户端知道哪些中继节点在线、可信、负载低。&lt;/p&gt;
&lt;h4 id=&#34;核心数据模型&#34;&gt;核心数据模型&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Node {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    node_id:        节点唯一 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    public_key:     Ed25519/X25519 公钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    endpoint:       公网 IP:UDP 端口
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bandwidth:      声明带宽（Mbps）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    load:           当前负载（连接数 / 带宽占用）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version:        协议版本
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flags:          入口 / 中间 / 出口 / 中继 标志位
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_seen:      最后一次心跳时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signature:      节点用私钥对以上字段签名
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;客户端获取节点列表的流程&#34;&gt;客户端获取节点列表的流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as Android Client
    participant D as Directory Server
    participant N1 as Node Entry
    participant N2 as Node Middle
    participant N3 as Node Exit

    C-&amp;gt;&amp;gt;D: HTTPS GET /nodes?count=3&amp;amp;flags=entry|middle|exit
    D--&amp;gt;&amp;gt;C: 返回节点列表 + 签名
    C-&amp;gt;&amp;gt;C: 验证签名、选择路径
    C-&amp;gt;&amp;gt;N1: UDP 建立 Circuit 请求
    N1--&amp;gt;&amp;gt;C: 返回 Circuit ID
    C-&amp;gt;&amp;gt;N1: 经过 N1 转发到 N2 建立
    N2--&amp;gt;&amp;gt;C: 返回确认
    C-&amp;gt;&amp;gt;N2: 经过 N1→N2 转发到 N3 建立
    N3--&amp;gt;&amp;gt;C: 返回确认&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：目录服务返回的节点列表必须带签名，客户端要校验，否则会被恶意节点钓鱼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-中继服务relay-server&#34;&gt;3.2 中继服务（Relay Server）&lt;/h3&gt;
&lt;p&gt;中继服务是洋葱网络里的「快递员」：收到一个 UDP 报文，解密最外层，转发给下一跳。&lt;/p&gt;
&lt;h4 id=&#34;核心状态&#34;&gt;核心状态&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Circuit {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    circuit_id:     本节点分配的 32 位 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    prev_node:      上一跳地址（IP:Port）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    next_node:      下一跳地址（IP:Port）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shared_key:     与创建者协商的共享密钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created_at:     创建时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_active:    最后活跃时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bytes_forward:  转发字节数
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;转发流程&#34;&gt;转发流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[收到 UDP 报文] --&amp;gt; B{报文格式合法?}
    B --&amp;gt;|否| C[直接丢弃]
    B --&amp;gt;|是| D[查找 circuit_id]
    D --&amp;gt; E{存在?}
    E --&amp;gt;|否| C
    E --&amp;gt;|是| F[用 shared_key 解密洋葱层]
    F --&amp;gt; G{解密成功?}
    G --&amp;gt;|否| C
    G --&amp;gt;|是| H[读取下一跳地址 + 内层数据]
    H --&amp;gt; I[TTL - 1]
    I --&amp;gt; J{TTL &amp;gt; 0?}
    J --&amp;gt;|否| K[发送销毁通知]
    J --&amp;gt;|是| L[转发给下一跳]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h4 id=&#34;关键设计点&#34;&gt;关键设计点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;限速与防滥用&lt;/strong&gt;：单 Circuit 限制带宽，防止被当免费代理滥用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL 与环检测&lt;/strong&gt;：每跳 TTL 减 1，到 0 丢弃，防止恶意构造环路。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;密钥派生&lt;/strong&gt;：每个 Circuit 用 X25519 ECDH 协商出共享密钥，再用 HKDF 派出发送/接收两个方向的对称密钥。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无日志策略&lt;/strong&gt;：中继节点不记录明文内容、不记录完整路径（实际上也看不到）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;33-信令服务signaling-server&#34;&gt;3.3 信令服务（Signaling Server）&lt;/h3&gt;
&lt;p&gt;信令服务是 P2P 的「媒人」：两个 Peer 无法直接找到对方公网地址时，通过信令服务交换信息。&lt;/p&gt;
&lt;h4 id=&#34;数据模型&#34;&gt;数据模型&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PeerInfo {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    peer_id:        用户/设备唯一 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    public_key:     用于加密信令的公钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    nat_type:       全锥型 / 限制型 / 端口限制型 / 对称型
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    local_candidates: [局域网 IP:Port, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    srflx_candidates: [STUN 映射后的公网 IP:Port, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    relay_candidates: [TURN 中继地址, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    timestamp:      更新时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signature:      防止伪造
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;典型信令流程&#34;&gt;典型信令流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant S as Signaling Server
    participant B as Peer B

    A-&amp;gt;&amp;gt;S: 注册/上报 PeerInfo（UDP 心跳包）
    B-&amp;gt;&amp;gt;S: 注册/上报 PeerInfo

    A-&amp;gt;&amp;gt;S: 请求连接 peer_B
    S-&amp;gt;&amp;gt;S: 检查 A、B 是否在线
    S--&amp;gt;&amp;gt;A: 返回 B 的候选地址列表
    S--&amp;gt;&amp;gt;B: 转发 A 的连接请求 + A 的候选地址

    A-&amp;gt;&amp;gt;A: 按优先级尝试直连 / 打洞
    B-&amp;gt;&amp;gt;B: 同时尝试直连 / 打洞
    A--&amp;gt;&amp;gt;B: UDP 打洞成功，建立 P2P
    B--&amp;gt;&amp;gt;A: 业务数据互通

    alt 打洞失败
        A-&amp;gt;&amp;gt;S: 请求中继
        S--&amp;gt;&amp;gt;A: 分配 Relay 地址
        S--&amp;gt;&amp;gt;B: 通知 B 通过 Relay 通信
        A-&amp;gt;&amp;gt;R: 经中继与 B 通信
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：信令服务本身不传输业务数据，只传「怎么找到对方」。不要把它当成聊天服务器用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-android-客户端设计&#34;&gt;4. Android 客户端设计&lt;/h2&gt;
&lt;p&gt;Android 客户端需要同时处理：业务逻辑、洋葱路由、UDP 网络、NAT 穿透、后台保活。建议按层拆分。&lt;/p&gt;
&lt;h3 id=&#34;41-客户端分层架构&#34;&gt;4.1 客户端分层架构&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 应用层
        UI[UI / ViewModel]
        API[业务 API]
    end

    subgraph 网络中间层
        CM[ConnectionManager&amp;lt;br/&amp;gt;管理多 Circuit / 多 Peer]
        P2P[P2PController&amp;lt;br/&amp;gt;打洞 / 直连 / 中继决策]
        SM[SignalingClient&amp;lt;br/&amp;gt;与信令服务通信]
    end

    subgraph 核心引擎层
        OC[OnionCore&amp;lt;br/&amp;gt;加密 / 解密 / 报文封装]
        NC[NetworkCore&amp;lt;br/&amp;gt;UDP Socket / ICE / STUN]
        CR[CircuitStore&amp;lt;br/&amp;gt;持久化 Circuit 状态]
    end

    UI --&amp;gt; API
    API --&amp;gt; CM
    CM --&amp;gt; P2P
    CM --&amp;gt; SM
    CM --&amp;gt; OC
    P2P --&amp;gt; OC
    P2P --&amp;gt; NC
    SM --&amp;gt; NC
    OC --&amp;gt; NC
    CM --&amp;gt; CR&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;42-关键模块说明&#34;&gt;4.2 关键模块说明&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;模块&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;职责&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;技术要点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;ConnectionManager&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;管理所有活跃 Circuit 和 Peer 连接&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;连接池、复用、超时清理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;P2PController&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;决定是否直连、打洞、还是走中继&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ICE 候选排序、NAT 类型判断&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;SignalingClient&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;与信令服务器通信&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP 心跳、请求/响应匹配、重传&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;OnionCore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;分层加密/解密、报文打包&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;X25519、ChaCha20-Poly1305、HKDF&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;NetworkCore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP Socket、STUN/ICE、NAT 类型探测&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;DatagramChannel、线程模型、电池优化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;CircuitStore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;本地缓存 Circuit 信息&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;SQLite / MMKV、加密存储&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;43-networkcore-线程模型&#34;&gt;4.3 NetworkCore 线程模型&lt;/h3&gt;
&lt;p&gt;UDP 网络层要避免在主线程做 IO，也避免一个线程处理所有事导致卡顿。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 网络线程
        RT[ReceiveThread&amp;lt;br/&amp;gt;阻塞 recvFrom]
        RT --&amp;gt;|Raw Packet| DP[DecoderPipeline&amp;lt;br/&amp;gt;STUN / 洋葱 / 业务]
        DP --&amp;gt;|业务消息| MQ[MessageQueue]
        DP --&amp;gt;|信令响应| SR[SignalingResponseMap]
    end

    subgraph 发送线程
        SQ[SendQueue&amp;lt;br/&amp;gt;优先级队列]
        ST[SendThread&amp;lt;br/&amp;gt;定时 flush]
        SQ --&amp;gt; ST --&amp;gt;|sendTo| Socket
    end

    subgraph 业务线程
        BH[BusinessHandler]
        MQ --&amp;gt; BH
    end

    Socket --&amp;gt; RT&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;44-android-端-nat-类型探测&#34;&gt;4.4 Android 端 NAT 类型探测&lt;/h3&gt;
&lt;p&gt;客户端启动时，先用 STUN 服务器探测自己的 NAT 类型：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1. 向 STUN Server A 发 Binding Request，得到映射地址 M1。
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2. 向 STUN Server B 发 Binding Request，得到映射地址 M2。
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3. 比较 M1 和 M2：
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   - 相同 → 可能全锥型 / 限制型
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   - 不同 → 对称型 NAT（打洞困难）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4. 再测试其他 IP/Port 是否能把包送进本机，判断是限制型还是端口限制型。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[开始探测] --&amp;gt; B[STUN A 请求]
    B --&amp;gt; C[得到 M1]
    C --&amp;gt; D[STUN B 请求]
    D --&amp;gt; E[得到 M2]
    E --&amp;gt; F{M1 == M2?}
    F --&amp;gt;|是| G[全锥 / 限制 / 端口限制]
    F --&amp;gt;|否| H[对称型 NAT]
    G --&amp;gt; I[进一步测试过滤规则]
    H --&amp;gt; J[P2P 大概率失败&amp;lt;br/&amp;gt;优先准备 Relay]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;45-android-后台保活与省电&#34;&gt;4.5 Android 后台保活与省电&lt;/h3&gt;
&lt;p&gt;P2P 需要客户端能被找到，但 Android 对后台限制越来越严。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;方案&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;适用场景&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;注意事项&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Foreground Service&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;需要长期在线&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;必须显示通知，用户可能反感&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;WorkManager 周期性心跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;非实时场景&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;最小间隔 15 分钟，无法保证即时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;FCM / 厂商推送&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;唤醒 App 建立连接&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;国内厂商通道需要适配&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活 socket + 心跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;与信令服务维持 NAT 映射&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;心跳间隔建议 15-30s， balancing 电量&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：不要试图用「双进程守护」「1 像素 Activity」等旧保活手段，Android 高版本会被杀，且应用商店可能下架。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-通过-udp-信令网络实现-p2p-控制&#34;&gt;5. 通过 UDP 信令网络实现 P2P 控制&lt;/h2&gt;
&lt;h3 id=&#34;51-p2p-连接的三种结果&#34;&gt;5.1 P2P 连接的三种结果&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[Peer A 想连 Peer B] --&amp;gt; B[通过信令服务获取 B 的候选地址]
    B --&amp;gt; C{尝试直连}
    C --&amp;gt;|成功| D[UDP P2P 直连]
    C --&amp;gt;|失败| E{尝试 NAT 打洞}
    E --&amp;gt;|成功| D
    E --&amp;gt;|失败| F[通过 Relay 中继]
    D --&amp;gt; G[业务通信]
    F --&amp;gt; G&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-ice-like-候选地址优先级&#34;&gt;5.2 ICE-like 候选地址优先级&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[直连候选&amp;lt;br/&amp;gt;局域网 IP] --&amp;gt;|优先级最高| B[srflx 候选&amp;lt;br/&amp;gt;STUN 映射公网地址]
    B --&amp;gt;|次高| C[prflx 候选&amp;lt;br/&amp;gt;对端看到的地址]
    C --&amp;gt;|再次| D[relay 候选&amp;lt;br/&amp;gt;TURN/中继服务器]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;优先级规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;host 候选&lt;/strong&gt;：同一局域网直接连，延迟最低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;server reflexive（srflx）&lt;/strong&gt;：STUN 映射后的公网地址，适合普通 NAT。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;peer reflexive（prflx）&lt;/strong&gt;：对端实际看到的地址，可能在打洞过程中发现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;relay 候选&lt;/strong&gt;：最后兜底，走服务器转发。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;53-nat-打洞详细流程&#34;&gt;5.3 NAT 打洞详细流程&lt;/h3&gt;
&lt;p&gt;假设 A 和 B 都在普通家用路由器后，NAT 类型为「端口限制锥型」。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant SA as STUN/Signaling Server
    participant B as Peer B

    A-&amp;gt;&amp;gt;SA: 注册：我的 srflx 是 1.1.1.1:10000
    B-&amp;gt;&amp;gt;SA: 注册：我的 srflx 是 2.2.2.2:20000

    A-&amp;gt;&amp;gt;SA: 请求连接 B
    SA--&amp;gt;&amp;gt;A: 返回 B 的地址 2.2.2.2:20000
    SA--&amp;gt;&amp;gt;B: 转发 A 的地址 1.1.1.1:10000

    A-&amp;gt;&amp;gt;B: 发送 UDP 打洞包（可能到达或被过滤）
    B-&amp;gt;&amp;gt;A: 发送 UDP 打洞包
    Note over A,B: 双方都先发包，让各自 NAT 建立映射

    A-&amp;gt;&amp;gt;B: 收到 B 的包，回确认
    B-&amp;gt;&amp;gt;A: 收到 A 的包，回确认
    Note over A,B: 打洞成功，后续直接通信&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人记忆点&lt;/strong&gt;：NAT 打洞的本质是&lt;strong&gt;让双方路由器先记录一条「允许对方包进来」的映射&lt;/strong&gt;。所以必须双方同时主动发一个包到对方公网地址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;54-基于洋葱网络的-p2p-控制通道&#34;&gt;5.4 基于洋葱网络的 P2P 控制通道&lt;/h3&gt;
&lt;p&gt;除了直接 P2P，还可以把「P2P 控制信令」本身也套在洋葱网络里，增强隐私：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt;|洋葱隧道| X[出口节点] --&amp;gt; S[信令服务器]
    B[Bob] --&amp;gt;|洋葱隧道| X&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;控制消息包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求连接、交换候选地址。&lt;/li&gt;
&lt;li&gt;协商加密密钥（用于 P2P 直连后的端到端加密）。&lt;/li&gt;
&lt;li&gt;心跳、断开通知。&lt;/li&gt;
&lt;li&gt;中继切换通知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样即使信令服务器被监控，攻击者也只能看到「某个洋葱出口在发信令」，不知道 Alice 和 Bob 的真实身份。&lt;/p&gt;
&lt;h3 id=&#34;55-p2p-直连后的加密&#34;&gt;5.5 P2P 直连后的加密&lt;/h3&gt;
&lt;p&gt;P2P 直连本身不保证安全，还需要端到端加密：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant S as Signaling Server（洋葱保护下）
    participant B as Peer B

    A-&amp;gt;&amp;gt;S: 发送 X25519 公钥 PA
    S-&amp;gt;&amp;gt;B: 转发 PA
    B-&amp;gt;&amp;gt;S: 发送 X25519 公钥 PB
    S-&amp;gt;&amp;gt;A: 转发 PB

    A-&amp;gt;&amp;gt;A: ECDH 计算共享密钥 K
    B-&amp;gt;&amp;gt;B: ECDH 计算共享密钥 K

    A-&amp;gt;&amp;gt;B: 用 K 加密业务数据，UDP P2P 直连发送
    B-&amp;gt;&amp;gt;A: 用 K 加密业务数据，UDP P2P 直连发送&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：洋葱网络的逐跳加密 ≠ P2P 端到端加密。如果数据离开出口节点后是明文，出口节点仍然能看到内容。真正保密需要在 P2P 直连后再做一次端到端加密。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-协议报文设计示例&#34;&gt;6. 协议报文设计示例&lt;/h2&gt;
&lt;h3 id=&#34;61-洋葱路由报文&#34;&gt;6.1 洋葱路由报文&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Version       | Command       | TTL           | Reserved        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                        Circuit ID                             |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+              Encrypted Onion Payload (variable)               +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;Command 定义示例：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;值&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;命令&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x01&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CREATE&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;建立 Circuit&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x02&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CREATED&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;建立成功响应&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x03&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;RELAY&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;中继数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x04&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;DESTROY&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;销毁 Circuit&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x05&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HEARTBEAT&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活心跳&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;62-信令报文&#34;&gt;6.2 信令报文&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Magic (4B)    | Version (1B)  | Command (1B)  | Flags (2B)    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         Sequence Number                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         Timestamp                             |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+                    Encrypted Payload                          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;信令 Command 示例：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;值&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;命令&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x10&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;REGISTER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;客户端注册&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x11&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;OFFER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;发起连接请求&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x12&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ANSWER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;响应连接请求&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x13&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CANDIDATE&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;交换候选地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x14&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HEARTBEAT&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x15&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ERROR&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;错误通知&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：UDP 报文要加 Magic 数和版本号，防止收到错乱的广播/扫描包时解析出错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-关键算法与数据结构&#34;&gt;7. 关键算法与数据结构&lt;/h2&gt;
&lt;h3 id=&#34;71-circuit-选择算法&#34;&gt;7.1 Circuit 选择算法&lt;/h3&gt;
&lt;p&gt;客户端从目录服务拿到节点列表后，选择一条路径：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;choose_path&lt;/span&gt;(nodes, desired_hops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 1. 过滤在线、版本兼容、flags 符合要求的节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    valid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [n &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; nodes &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_online &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; PROTO_VERSION]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 2. 按负载和带宽评分，负载越低、带宽越高分越高&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scored &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(n, score(n)) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; valid]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scored&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sort(key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; x: x[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], reverse&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 3. 随机选择前 K 个中的节点，避免总是选同一批&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    candidates &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scored[:max(len(scored)&lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, desired_hops &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 4. 确保 Entry、Middle、Exit 来自不同运营商 / 网段，增加路径多样性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; role &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;entry&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;middle&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chosen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pick_diverse(path, candidates, role)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(chosen)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;72-消息可靠性保证&#34;&gt;7.2 消息可靠性保证&lt;/h3&gt;
&lt;p&gt;UDP 不保证可靠，应用层需要自己实现：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[发送消息] --&amp;gt; B[分配 Seq]
    B --&amp;gt; C[加入重传队列]
    C --&amp;gt; D[UDP 发送]
    D --&amp;gt; E{收到 ACK?}
    E --&amp;gt;|是| F[从队列移除]
    E --&amp;gt;|否| G{超时?}
    G --&amp;gt;|否| E
    G --&amp;gt;|是| H[重传 N 次]
    H --&amp;gt; I{达到最大重试?}
    I --&amp;gt;|否| C
    I --&amp;gt;|是| J[通知上层失败]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;重传策略建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始超时：200ms（RTT 估计）。&lt;/li&gt;
&lt;li&gt;退避：指数退避，最多 5 次。&lt;/li&gt;
&lt;li&gt;ACK 去重：用滑动窗口或位图记录已确认 Seq。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;73-连接状态机&#34;&gt;7.3 连接状态机&lt;/h3&gt;
&lt;p&gt;P2P 连接可以建模为状态机：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; Idle: 用户发起连接
    Idle --&amp;gt; Gathering: 收集 ICE 候选
    Gathering --&amp;gt; Connecting: 开始打洞/直连
    Connecting --&amp;gt; Connected: 收到对端包
    Connecting --&amp;gt; Relaying: 打洞失败，切中继
    Relaying --&amp;gt; Connected: 中继链路建立
    Connected --&amp;gt; Disconnected: 超时未收到心跳
    Disconnected --&amp;gt; [*]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;8-新人避坑技巧大全&#34;&gt;8. 新人避坑技巧大全&lt;/h2&gt;
&lt;h3 id=&#34;81-udp-层面的坑&#34;&gt;8.1 UDP 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 报文丢失&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;部分消息收不到&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;应用层 ACK + 重传；关键信令走可靠通道&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 乱序&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;消息顺序错乱&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;按 Seq 排序或丢弃过期包；业务层幂等&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 分片&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;大报文丢失率陡增&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;控制报文大小 &amp;lt; 1200 字节；必要时分片重组&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;运营商 QoS&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;高峰丢包严重&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;使用常见端口（如 443/8080）或混淆成 QUIC/DNS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;防火墙拦截&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;某些网络 UDP 不通&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;准备 TCP 回退或 Relay 兜底&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;82-洋葱网络层面的坑&#34;&gt;8.2 洋葱网络层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Circuit 被重用&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;旧连接和新连接混用&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每个 Circuit 带唯一 ID，过期销毁&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;时序攻击&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;入口和出口流量时间关联&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;引入填充（padding）和混合延迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;恶意出口节点&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;出口监听或篡改明文&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;P2P 必须端到端加密，不依赖出口&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;节点列表被污染&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;连上恶意节点&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;目录服务签名 + 节点身份固定 + 信誉系统&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;密钥管理混乱&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;加密失败或性能差&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一个 Circuit 一对密钥，用 HKDF 派生&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;83-android-层面的坑&#34;&gt;8.3 Android 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;主线程网络&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ANR / 卡顿&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;所有 UDP IO 放子线程或用协程&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Doze 模式心跳中断&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;后台连接断开&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;使用 JobScheduler / WorkManager 补心跳&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;IPv4/IPv6 双栈&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一端 v4 一端 v6 连不上&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;同时收集 v4/v6 候选地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;端口复用失败&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;BindException&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Socket 设置 &lt;code&gt;SO_REUSEADDR&lt;/code&gt; / &lt;code&gt;SO_REUSEPORT&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;电量消耗高&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;用户卸载&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;动态心跳间隔，网络空闲时延长&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;84-p2p--nat-层面的坑&#34;&gt;8.4 P2P / NAT 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;对称型 NAT 打洞失败&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;两边都收不到包&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;提前探测 NAT 类型，准备 Relay&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;** hairpin NAT 不支持**&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;同一局域网却走公网&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;优先尝试 host 候选&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;NAT 映射超时&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;长时间没通信后连不上&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;定期发送心跳保活&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;双方不同时打洞&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一方先发可能被防火墙拦&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;设计让 A、B 同时主动发包&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;信令与数据通道混淆&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;安全边界不清&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;信令用短期密钥，数据用长期会话密钥&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;85-安全与隐私层面的坑&#34;&gt;8.5 安全与隐私层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;只做了洋葱加密，没做端到端加密&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;出口节点可读明文&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;P2P 直连后再做一次 ECDH&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;时间戳不校验&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;重放攻击&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每个报文带时间戳 + 滑动窗口&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;随机数可预测&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Circuit ID 被猜中&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;用 SecureRandom / crypto.random&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;元数据泄露&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;包大小暴露内容类型&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;固定包长或随机填充&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;证书/公钥不校验&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;中间人攻击&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;目录服务签名 + 证书锁定&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;9-一个最小可运行流程串起来&#34;&gt;9. 一个最小可运行流程串起来&lt;/h2&gt;
&lt;p&gt;为了让新人有整体感，下面把一次「Alice 通过 UDP 洋葱网络控制 P2P 连接 Bob」的完整流程串起来。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Android Alice
    participant D as Directory Server
    participant S as Signaling Server
    participant R as Relay Server
    participant B as Android Bob

    %% 阶段 1：准备节点
    A-&amp;gt;&amp;gt;D: HTTPS 获取节点列表
    D--&amp;gt;&amp;gt;A: 返回 Entry/Middle/Exit + 签名
    A-&amp;gt;&amp;gt;A: 校验签名，选择路径
    A-&amp;gt;&amp;gt;Entry: UDP CREATE Circuit
    Entry--&amp;gt;&amp;gt;A: CREATED
    A-&amp;gt;&amp;gt;Middle: 经 Entry 转发 CREATE
    Middle--&amp;gt;&amp;gt;A: CREATED
    A-&amp;gt;&amp;gt;Exit: 经 Entry→Middle 转发 CREATE
    Exit--&amp;gt;&amp;gt;A: CREATED

    %% 阶段 2：注册信令
    A-&amp;gt;&amp;gt;S: 经洋葱隧道 REGISTER Alice
    B-&amp;gt;&amp;gt;S: 经洋葱隧道 REGISTER Bob

    %% 阶段 3：发起 P2P
    A-&amp;gt;&amp;gt;S: OFFER（请求连接 Bob）
    S-&amp;gt;&amp;gt;B: 转发 OFFER + Alice 候选地址
    B-&amp;gt;&amp;gt;S: ANSWER + Bob 候选地址
    S-&amp;gt;&amp;gt;A: 转发 ANSWER

    %% 阶段 4：打洞
    A-&amp;gt;&amp;gt;B: UDP 打洞包
    B-&amp;gt;&amp;gt;A: UDP 打洞包
    A--&amp;gt;&amp;gt;B: 直连成功

    %% 阶段 5：业务通信
    A-&amp;gt;&amp;gt;B: 端到端加密业务数据
    B-&amp;gt;&amp;gt;A: 端到端加密业务数据

    %% 阶段 6：失败兜底
    alt 打洞失败
        A-&amp;gt;&amp;gt;S: 请求 Relay
        S--&amp;gt;&amp;gt;A: 分配 Relay 地址
        S--&amp;gt;&amp;gt;B: 通知走 Relay
        A-&amp;gt;&amp;gt;R: 经洋葱→Relay→Bob
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;10-总结一张图记住全文&#34;&gt;10. 总结：一张图记住全文&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    A[UDP 洋葱网络 + P2P] --&amp;gt; B[目录服务&amp;lt;br/&amp;gt;给节点]
    A --&amp;gt; C[信令服务&amp;lt;br/&amp;gt;撮合 Peer]
    A --&amp;gt; D[中继服务&amp;lt;br/&amp;gt;兜底转发]
    A --&amp;gt; E[Android 客户端&amp;lt;br/&amp;gt;洋葱 + NAT + P2P]
    E --&amp;gt; F[OnionCore&amp;lt;br/&amp;gt;逐跳加密]
    E --&amp;gt; G[NetworkCore&amp;lt;br/&amp;gt;UDP / STUN / ICE]
    E --&amp;gt; H[P2PController&amp;lt;br/&amp;gt;直连 / 打洞 / 中继]
    F --&amp;gt; I[隐私保护]
    G --&amp;gt; J[NAT 穿透]
    H --&amp;gt; K[去中心化通信]
    A --&amp;gt; L[避坑：可靠传输 / 端到端加密 / 后台保活]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;核心要点回顾：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;洋葱网络用 UDP 是为了轻量、NAT 友好、适合信令&lt;/strong&gt;；但可靠性和顺序要自己补。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务器分三类&lt;/strong&gt;：目录服务（给节点）、信令服务（撮合 P2P）、中继服务（兜底转发）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android 客户端分层&lt;/strong&gt;：业务层、连接管理层、核心引擎层、网络层，不要混在一处。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P2P 控制靠 UDP 信令交换地址&lt;/strong&gt;，再用 ICE 候选优先级尝试直连、打洞、中继。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐私不等于安全&lt;/strong&gt;：洋葱网络保护路径隐私，P2P 业务数据还要端到端加密。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;新人最容易踩的坑&lt;/strong&gt;：UDP 当 TCP 用、忽略 NAT 类型、不做端到端加密、Android 主线程 IO。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考与延伸阅读&#34;&gt;参考与延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tor Project: &lt;a href=&#34;https://www.torproject.org/&#34;&gt;https://www.torproject.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ICE / STUN / TURN RFCs: RFC 5245, RFC 5389, RFC 5766&lt;/li&gt;
&lt;li&gt;WebRTC NAT Traversal 实践&lt;/li&gt;
&lt;li&gt;Android &lt;code&gt;DatagramSocket&lt;/code&gt;、&lt;code&gt;WorkManager&lt;/code&gt;、&lt;code&gt;Foreground Service&lt;/code&gt; 官方文档&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你正在动手实现，建议先做一个「最小闭环」：一个目录服务 + 一个中继节点 + 两个 Android 客户端，先跑通「Alice 经洋葱网络发一条消息给 Bob」，再逐步加入 P2P 直连和 NAT 打洞。先跑通，再优化。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title> TCP/IP 学习总结与避坑指南</title>
      <link>https://ftmao.com/blog/bookstore/tcp-ip-illustrated-notes/</link>
      <pubDate>Sat, 09 Dec 2017 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/tcp-ip-illustrated-notes/</guid>
      <description>&lt;h1 id=&#34;tcpip-学习总结与避坑指南&#34;&gt;TCP/IP 学习总结与避坑指南&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;最近华为项目交付碰到各种头大的网络问题，现在整理下TCP/IP相关知识。参考书籍：W. Richard Stevens《TCP/IP 详解 卷1：协议》、W. Richard Stevens《Unix 网络编程 卷1：套接字联网 API》&lt;br&gt;
目标读者：刚接触网络编程，或者学了很多概念但总觉得「协议栈串不起来」「代码一跑就掉坑」的同学。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TCP/IP 不是一门「背完就能会」的技术。它由一整套分层协议组成，每一层只关心自己的事情，又通过严格定义的头部把信息交给下一层。理解这套分层思维，是看懂网络报文、写出稳定网络程序、排查线上连接问题的第一步。&lt;/p&gt;
&lt;p&gt;这篇文章不会把所有 RFC 都讲一遍（那是 1000 多页书做的事），而是帮你建立&lt;strong&gt;主干知识框架&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TCP/IP 协议栈为什么要分层？数据是怎么一层层包起来的？&lt;/li&gt;
&lt;li&gt;IP、TCP、UDP、ARP、ICMP、DNS 这些协议各自解决什么问题？&lt;/li&gt;
&lt;li&gt;Unix Socket 编程的核心 API 与调用顺序是什么？&lt;/li&gt;
&lt;li&gt;写网络程序最容易踩哪些坑，怎么避开？&lt;/li&gt;
&lt;li&gt;遇到连接失败、卡顿、端口耗尽这些问题，该按什么思路排查？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;读完之后，你应该能看懂 Wireshark 里的大部分常见报文，也能独立写出稳定的 TCP 客户端/服务端，并具备基础的线上网络问题排查能力。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一tcpip-协议栈为什么必须分层&#34;&gt;一、TCP/IP 协议栈：为什么必须分层？&lt;/h2&gt;
&lt;p&gt;网络通信的复杂度非常高：不同的物理介质、不同的操作系统、不同的应用程序要在全球范围内交换数据。如果不分层，每一台设备都要同时处理「电信号怎么传」「数据怎么路由」「传输可不可靠」「应用数据什么意思」所有问题。&lt;/p&gt;
&lt;p&gt;分层的目的就是&lt;strong&gt;关注点分离&lt;/strong&gt;：每一层只解决本层的问题，向上层提供服务，把复杂问题拆成可管理的小块。&lt;/p&gt;
&lt;h3 id=&#34;11-osi-七层模型-vs-tcpip-四层模型&#34;&gt;1.1 OSI 七层模型 vs TCP/IP 四层模型&lt;/h3&gt;
&lt;p&gt;教学中常用 OSI 七层模型，但互联网实际运行的是 TCP/IP 四层模型。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    subgraph OSI七层模型
        A7[7 应用层] --&amp;gt; A6[6 表示层]
        A6 --&amp;gt; A5[5 会话层]
        A5 --&amp;gt; A4[4 传输层]
        A4 --&amp;gt; A3[3 网络层]
        A3 --&amp;gt; A2[2 数据链路层]
        A2 --&amp;gt; A1[1 物理层]
    end

    subgraph TCP/IP四层模型
        B4[应用层&amp;lt;br/&amp;gt;HTTP/FTP/DNS/SMTP] --&amp;gt; B3[传输层&amp;lt;br/&amp;gt;TCP/UDP]
        B3 --&amp;gt; B2[网络层&amp;lt;br/&amp;gt;IP/ICMP/IGMP]
        B2 --&amp;gt; B1[网络接口层&amp;lt;br/&amp;gt;以太网/WiFi/PPP]
    end

    A7 -.对应.-&amp;gt; B4
    A6 -.对应.-&amp;gt; B4
    A5 -.对应.-&amp;gt; B4
    A4 -.对应.-&amp;gt; B3
    A3 -.对应.-&amp;gt; B2
    A2 -.对应.-&amp;gt; B1
    A1 -.对应.-&amp;gt; B1&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;TCP/IP 层&lt;/th&gt;
					&lt;th&gt;核心协议&lt;/th&gt;
					&lt;th&gt;解决的问题&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;应用层&lt;/td&gt;
					&lt;td&gt;HTTP、DNS、FTP、SMTP、SSH&lt;/td&gt;
					&lt;td&gt;应用程序之间「说什么」&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;传输层&lt;/td&gt;
					&lt;td&gt;TCP、UDP&lt;/td&gt;
					&lt;td&gt;进程到进程的通信，可靠或高效传输&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;网络层&lt;/td&gt;
					&lt;td&gt;IP、ICMP、IGMP、路由协议&lt;/td&gt;
					&lt;td&gt;主机到主机的寻址与路由&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;网络接口层&lt;/td&gt;
					&lt;td&gt;以太网、WiFi、ARP、PPP&lt;/td&gt;
					&lt;td&gt;相邻设备之间的物理/逻辑传输&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;新手容易混淆的点&lt;/strong&gt;：TCP/IP 里的「应用层」实际上对应了 OSI 的应用层、表示层、会话层三层。不要死磕七层，实际工程里四层模型更常用。&lt;/p&gt;
&lt;h3 id=&#34;12-数据封装与解封装&#34;&gt;1.2 数据封装与解封装&lt;/h3&gt;
&lt;p&gt;每一层都会给上一层的数据加一个「头部」，然后交给下一层。接收方则逐层去掉头部，把数据交给上层。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 发送方
        A[应用数据] --&amp;gt;|&amp;#34;应用层封装&amp;#34;| B[&amp;#34;应用层头部 + 应用数据&amp;#34;]
        B --&amp;gt;|&amp;#34;传输层封装&amp;#34;| C[&amp;#34;TCP/UDP 头部 + 应用层数据&amp;#34;]
        C --&amp;gt;|&amp;#34;网络层封装&amp;#34;| D[&amp;#34;IP 头部 + TCP 数据段&amp;#34;]
        D --&amp;gt;|&amp;#34;链路层封装&amp;#34;| E[&amp;#34;以太网头部 + IP 数据报 + 以太网尾部&amp;#34;]
    end

    E --&amp;gt;|&amp;#34;比特流在物理介质传输&amp;#34;| F

    subgraph 接收方
        F[&amp;#34;以太网帧&amp;#34;] --&amp;gt;|&amp;#34;链路层解封装&amp;#34;| G[&amp;#34;IP 数据报&amp;#34;]
        G --&amp;gt;|&amp;#34;网络层解封装&amp;#34;| H[&amp;#34;TCP/UDP 数据段&amp;#34;]
        H --&amp;gt;|&amp;#34;传输层解封装&amp;#34;| I[&amp;#34;应用层数据&amp;#34;]
        I --&amp;gt;|&amp;#34;应用层处理&amp;#34;| J[交给对应进程]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键概念&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MTU（Maximum Transmission Unit）&lt;/strong&gt;：链路层帧能承载的最大数据量，以太网常见值是 1500 字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MSS（Maximum Segment Size）&lt;/strong&gt;：TCP 一次能发送的最大数据段，通常 = MTU - IP 头 - TCP 头，以太网常见 1460 字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分片（Fragmentation）&lt;/strong&gt;：当 IP 数据报超过链路 MTU 时，IP 层会把它切成多个片。TCP 一般会通过 MSS 避免分片，UDP 则容易触发分片。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要随意修改 MTU，VPN/隧道场景特别容易出现「大数据包被静默丢弃」的问题。&lt;/li&gt;
&lt;li&gt;UDP 应用如果要传输大报文，建议在应用层自己拆包，不要让 IP 层分片。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二链路层与-arp同一局域网怎么找到对方&#34;&gt;二、链路层与 ARP：同一局域网怎么找到对方？&lt;/h2&gt;
&lt;p&gt;链路层负责&lt;strong&gt;相邻节点&lt;/strong&gt;之间的数据传输。在以太网里，设备用 MAC 地址标识，交换机根据 MAC 地址转发帧。&lt;/p&gt;
&lt;h3 id=&#34;21-以太网帧结构&#34;&gt;2.1 以太网帧结构&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| 目的 MAC 6B    | 源 MAC 6B      | 类型 2B        | 数据 46~1500B  | FCS 4B         |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+----------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MAC 地址&lt;/strong&gt;：48 位，全球唯一（理论上），用于局域网内定位网卡。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;类型字段&lt;/strong&gt;：例如 &lt;code&gt;0x0800&lt;/code&gt; 表示上层是 IP，&lt;code&gt;0x0806&lt;/code&gt; 表示 ARP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FCS（Frame Check Sequence）&lt;/strong&gt;：帧校验序列，检测传输过程中的比特错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-arpip-地址--mac-地址&#34;&gt;2.2 ARP：IP 地址 → MAC 地址&lt;/h3&gt;
&lt;p&gt;ARP（Address Resolution Protocol）解决的是「知道对方 IP，但不知道对方 MAC」的问题。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as 主机 A&amp;lt;br/&amp;gt;192.168.1.10
    participant B as 主机 B&amp;lt;br/&amp;gt;192.168.1.20

    A-&amp;gt;&amp;gt;B: ARP 请求：谁的 IP 是 192.168.1.20？请告诉我你的 MAC
    Note over A,B: 广播帧，局域网内所有主机收到
    B-&amp;gt;&amp;gt;A: ARP 应答：我是 192.168.1.20，MAC 是 xx:xx:xx:xx:xx:xx
    Note over A,B: 单播帧，只有 A 收到&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;ARP 表会缓存一段时间：&lt;code&gt;arp -a&lt;/code&gt; 可以查看本机 ARP 缓存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ARP 只在&lt;strong&gt;同一二层网络&lt;/strong&gt;（同一广播域）有效。跨网段通信时，主机把报文发给网关，由网关继续转发。&lt;/li&gt;
&lt;li&gt;网关配错是新手常见问题：如果默认网关不在同一子网，ARP 请求会失败，数据根本发不出去。&lt;/li&gt;
&lt;li&gt;ARP 欺骗/中间人攻击在局域网内存在，敏感环境要开启动态 ARP 检测（DAI）或静态绑定。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三ip-协议主机到主机的寻址与路由&#34;&gt;三、IP 协议：主机到主机的寻址与路由&lt;/h2&gt;
&lt;p&gt;IP（Internet Protocol）是网络层的核心，负责把数据从源主机送到目的主机。它&lt;strong&gt;不保证可靠、不保证有序&lt;/strong&gt;，这些由 TCP 或应用层自己处理。&lt;/p&gt;
&lt;h3 id=&#34;31-ipv4-头部核心字段&#34;&gt;3.1 IPv4 头部核心字段&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|Version|  IHL  |Type of Service|          Total Length         |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|         Identification        |Flags|      Fragment Offset    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  Time to Live |    Protocol   |         Header Checksum       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                       Source Address                          |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                    Destination Address                        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                    Options                    |    Padding    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;关键字段含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Version&lt;/strong&gt;：IPv4 填 4，IPv6 填 6。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IHL（Header Length）&lt;/strong&gt;：头部长度，单位是 4 字节。没有选项时最小是 5，即 20 字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total Length&lt;/strong&gt;：整个 IP 数据报的长度，最大 65535 字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Identification / Flags / Fragment Offset&lt;/strong&gt;：用于分片和重组。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL（Time to Live）&lt;/strong&gt;：每经过一个路由器减 1，到 0 被丢弃，防止报文在网络里无限循环。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protocol&lt;/strong&gt;：上层协议类型。6 = TCP，17 = UDP，1 = ICMP，等等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Source/Destination Address&lt;/strong&gt;：源 IP 和目的 IP。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;32-ip-地址子网掩码与-cidr&#34;&gt;3.2 IP 地址、子网掩码与 CIDR&lt;/h3&gt;
&lt;p&gt;IP 地址 = 网络号 + 主机号。子网掩码用来划分这两部分。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;IP 地址：    192.168.1.100   → 11000000.10101000.00000001.01100100
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;子网掩码：   255.255.255.0   → 11111111.11111111.11111111.00000000
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;网络号：     192.168.1.0     → 前 24 位
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;主机号：     0.0.0.100       → 后 8 位&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;CIDR 表示法：&lt;code&gt;192.168.1.0/24&lt;/code&gt;，斜杠后面的数字表示网络号位数。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断两台主机是否在同一子网&lt;/strong&gt;：用各自的 IP 和子网掩码做按位与，得到网络号，网络号相同就在同一子网。&lt;/p&gt;
&lt;h3 id=&#34;33-路由基础&#34;&gt;3.3 路由基础&lt;/h3&gt;
&lt;p&gt;当目的 IP 不在本子网时，主机会把报文发给&lt;strong&gt;默认网关&lt;/strong&gt;（Default Gateway）。路由器根据路由表决定下一跳。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[主机 A&amp;lt;br/&amp;gt;192.168.1.10/24] --&amp;gt;|&amp;#34;目的 IP 不在同一子网&amp;#34;| R[路由器&amp;lt;br/&amp;gt;192.168.1.1]
    R --&amp;gt;|&amp;#34;查路由表&amp;#34;| B[主机 B&amp;lt;br/&amp;gt;10.0.0.5/24]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;常用查看命令：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Linux/macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ip route show        &lt;span style=&#34;color:#75715e&#34;&gt;# 查看路由表&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;route -n             &lt;span style=&#34;color:#75715e&#34;&gt;# 旧命令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;route print&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;配置静态 IP 时，最容易漏掉或配错的就是&lt;strong&gt;子网掩码&lt;/strong&gt;和&lt;strong&gt;默认网关&lt;/strong&gt;。只配 IP 不配上两者，通常只能 ping 通同网段。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/24&lt;/code&gt; 和 &lt;code&gt;/16&lt;/code&gt; 的区别不只是数字大小，它决定了网络规模和可用主机数。&lt;code&gt;/24&lt;/code&gt; 有 254 个可用主机地址，&lt;code&gt;/16&lt;/code&gt; 有 65534 个。&lt;/li&gt;
&lt;li&gt;路由表顺序很重要：更具体的子网（前缀更长）优先于默认路由。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四icmpip-的诊断助手&#34;&gt;四、ICMP：IP 的「诊断助手」&lt;/h2&gt;
&lt;p&gt;ICMP（Internet Control Message Protocol）本身不传输用户数据，而是用于&lt;strong&gt;传递控制消息和诊断信息&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;41-ping-原理&#34;&gt;4.1 Ping 原理&lt;/h3&gt;
&lt;p&gt;Ping 使用 ICMP Echo Request 和 Echo Reply。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as 主机 A
    participant B as 主机 B

    A-&amp;gt;&amp;gt;B: ICMP Echo Request
    Note over A,B: 携带标识符和序列号
    B-&amp;gt;&amp;gt;A: ICMP Echo Reply
    Note over A,B: A 根据往返时间计算延迟&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;Ping 不通不一定是对端关机，可能是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;防火墙拦截了 ICMP&lt;/li&gt;
&lt;li&gt;路由不可达&lt;/li&gt;
&lt;li&gt;对端 ARP 表异常&lt;/li&gt;
&lt;li&gt;本机默认网关配错&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;42-traceroute-原理&#34;&gt;4.2 Traceroute 原理&lt;/h3&gt;
&lt;p&gt;Traceroute 利用 TTL 递增或端口不可达来追踪路径。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[主机] --&amp;gt;|&amp;#34;TTL=1&amp;#34;| R1[路由器 1]
    R1 --&amp;gt;|&amp;#34;TTL 超时，返回 ICMP Time Exceeded&amp;#34;| A
    A --&amp;gt;|&amp;#34;TTL=2&amp;#34;| R2[路由器 2]
    R2 --&amp;gt;|&amp;#34;TTL 超时，返回 ICMP Time Exceeded&amp;#34;| A
    A --&amp;gt;|&amp;#34;TTL=3&amp;#34;| B[目标主机]
    B --&amp;gt;|&amp;#34;端口不可达 / Echo Reply&amp;#34;| A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有些路由器配置了「不回应 ICMP Time Exceeded」，Traceroute 会显示 &lt;code&gt;* * *&lt;/code&gt;，不代表网络不通。&lt;/li&gt;
&lt;li&gt;防火墙可能会阻断 Traceroute 的 UDP 探测端口，换 &lt;code&gt;-I&lt;/code&gt;（ICMP）或 &lt;code&gt;-T&lt;/code&gt;（TCP）模式可能成功。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五udp简单高效不可靠&#34;&gt;五、UDP：简单、高效、不可靠&lt;/h2&gt;
&lt;p&gt;UDP（User Datagram Protocol）提供&lt;strong&gt;无连接、不可靠、面向数据报&lt;/strong&gt;的传输服务。&lt;/p&gt;
&lt;h3 id=&#34;51-udp-头部&#34;&gt;5.1 UDP 头部&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0      7 8     15 16    23 24    31
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+--------+--------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|     Source Port    |   Dest Port   |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+--------+--------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|     Length         |   Checksum    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+--------+--------+--------+--------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|          data octets ...            |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Source Port / Dest Port&lt;/strong&gt;：各 16 位，标识发送和接收进程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Length&lt;/strong&gt;：UDP 头部 + 数据的总长度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checksum&lt;/strong&gt;：可选（IPv4 中），覆盖头部、数据和伪头部。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;52-udp-的特点与适用场景&#34;&gt;5.2 UDP 的特点与适用场景&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;特点&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;无连接&lt;/td&gt;
					&lt;td&gt;不需要建立连接，直接发&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;不可靠&lt;/td&gt;
					&lt;td&gt;不保证到达、不保证顺序、不重传&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;低开销&lt;/td&gt;
					&lt;td&gt;头部只有 8 字节&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;面向数据报&lt;/td&gt;
					&lt;td&gt;发一次收一次，保留边界&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;适合用 UDP 的场景&lt;/strong&gt;：DNS 查询、视频直播、在线游戏、VoIP、NTP、QUIC 底层。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;UDP 没有流量控制，发送方太快会导致接收方丢包。实时音视频要做抖动缓冲（jitter buffer）和前向纠错（FEC）。&lt;/li&gt;
&lt;li&gt;UDP 报文超过 MTU 会被 IP 层分片，分片丢失会导致整个 UDP 报文作废。大数据包要在应用层分片。&lt;/li&gt;
&lt;li&gt;不要认为 UDP 一定比 TCP 快。在丢包率高的网络里，应用层自己实现可靠传输可能比 TCP 更复杂且效果更差。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六tcp可靠传输的基石&#34;&gt;六、TCP：可靠传输的基石&lt;/h2&gt;
&lt;p&gt;TCP（Transmission Control Protocol）是互联网最常用的传输协议。它提供&lt;strong&gt;面向连接、可靠、按序、字节流&lt;/strong&gt;的服务。&lt;/p&gt;
&lt;h3 id=&#34;61-tcp-头部核心字段&#34;&gt;6.1 TCP 头部核心字段&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|          Source Port          |       Destination Port        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                        Sequence Number                        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                    Acknowledgment Number                      |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  Data |           |U|A|P|R|S|F|                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Offset| Reserved  |R|C|S|S|Y|I|            Window             |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|       |           |G|K|H|T|N|N|                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|           Checksum            |         Urgent Pointer        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                    Options                    |    Padding    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;关键字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sequence Number（序号）&lt;/strong&gt;：标识本报文段发送的数据在字节流中的位置。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Acknowledgment Number（确认号）&lt;/strong&gt;：期望收到的下一个字节的序号。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ACK&lt;/strong&gt;：确认号有效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SYN&lt;/strong&gt;：发起连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;FIN&lt;/strong&gt;：结束连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RST&lt;/strong&gt;：重置连接，通常表示异常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Window（窗口大小）&lt;/strong&gt;：接收方当前可用缓存大小，用于流量控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;62-三次握手建立连接&#34;&gt;6.2 三次握手：建立连接&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as 客户端
    participant S as 服务端

    C-&amp;gt;&amp;gt;S: SYN, seq=x
    Note over C,S: 客户端请求建立连接
    S-&amp;gt;&amp;gt;C: SYN, seq=y, ACK=x+1
    Note over C,S: 服务端同意，也请求建立反向连接
    C-&amp;gt;&amp;gt;S: ACK=y+1
    Note over C,S: 客户端确认，连接建立&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么是三次？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;两次握手无法确认客户端的接收能力，也不能防止历史重复连接请求造成资源浪费。&lt;/li&gt;
&lt;li&gt;三次是「双方都能发送和接收」的最小验证次数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;关键状态&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客户端：SYN_SENT → ESTABLISHED&lt;/li&gt;
&lt;li&gt;服务端：LISTEN → SYN_RCVD → ESTABLISHED&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;63-四次挥手关闭连接&#34;&gt;6.3 四次挥手：关闭连接&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as 客户端
    participant S as 服务端

    C-&amp;gt;&amp;gt;S: FIN, seq=u
    Note over C,S: 客户端没有数据要发了
    S-&amp;gt;&amp;gt;C: ACK=u+1
    Note over C,S: 服务端确认，但可能还有数据要发
    S-&amp;gt;&amp;gt;C: FIN, seq=w
    Note over C,S: 服务端也没有数据要发了
    C-&amp;gt;&amp;gt;S: ACK=w+1
    Note over C,S: 客户端确认&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么一般是四次？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TCP 是全双工的，两个方向要分别关闭。一方发完 FIN 只表示「我这方向没数据了」，另一方可能还有数据要发。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键状态与时间等待&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主动关闭方最后进入 &lt;strong&gt;TIME_WAIT&lt;/strong&gt;，等待 2MSL（Maximum Segment Lifetime，通常 1-4 分钟）后才彻底关闭。&lt;/li&gt;
&lt;li&gt;TIME_WAIT 的作用：
&lt;ol&gt;
&lt;li&gt;确保最后一个 ACK 被对方收到，如果丢失可以重传。&lt;/li&gt;
&lt;li&gt;让网络中延迟的报文自然消失，避免影响下一个使用相同四元组的新连接。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;64-tcp-状态机&#34;&gt;6.4 TCP 状态机&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; CLOSED
    CLOSED --&amp;gt; SYN_SENT : 主动打开/send SYN
    CLOSED --&amp;gt; LISTEN : 被动打开
    SYN_SENT --&amp;gt; ESTABLISHED : 收到 SYN+ACK
    LISTEN --&amp;gt; SYN_RCVD : 收到 SYN
    SYN_RCVD --&amp;gt; ESTABLISHED : 收到 ACK
    ESTABLISHED --&amp;gt; FIN_WAIT_1 : 主动关闭/send FIN
    ESTABLISHED --&amp;gt; CLOSE_WAIT : 收到 FIN
    FIN_WAIT_1 --&amp;gt; FIN_WAIT_2 : 收到 ACK
    FIN_WAIT_1 --&amp;gt; TIME_WAIT : 收到 FIN+ACK
    FIN_WAIT_2 --&amp;gt; TIME_WAIT : 收到 FIN
    CLOSE_WAIT --&amp;gt; LAST_ACK : 发送 FIN
    LAST_ACK --&amp;gt; CLOSED : 收到 ACK
    TIME_WAIT --&amp;gt; CLOSED : 2MSL 超时&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;65-可靠传输机制&#34;&gt;6.5 可靠传输机制&lt;/h3&gt;
&lt;p&gt;TCP 通过以下机制保证可靠性：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[序号与确认机制] --&amp;gt; B[超时重传]
    C[滑动窗口] --&amp;gt; D[流量控制]
    E[拥塞窗口] --&amp;gt; F[拥塞控制]
    B --&amp;gt; G[可靠传输]
    D --&amp;gt; G
    F --&amp;gt; G&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;序号与确认&lt;/strong&gt;：每发一个字节都有序号，接收方用 ACK 确认连续收到的最大序号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;超时重传&lt;/strong&gt;：发送方启动定时器，超时未收到 ACK 就重传。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;滑动窗口&lt;/strong&gt;：允许发送方在没收到 ACK 的情况下连续发送多个段，提高吞吐量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;流量控制&lt;/strong&gt;：接收方通过 Window 字段告诉发送方自己还能接收多少数据，防止接收方缓存溢出。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;拥塞控制&lt;/strong&gt;：发送方根据网络拥塞程度调整发送速率，主要算法包括慢启动、拥塞避免、快重传、快恢复。&lt;/p&gt;
&lt;h3 id=&#34;66-拥塞控制四个阶段&#34;&gt;6.6 拥塞控制四个阶段&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    A[慢启动&amp;lt;br/&amp;gt;cwnd 指数增长] --&amp;gt;|&amp;#34;cwnd &amp;gt;= ssthresh&amp;#34;| B[拥塞避免&amp;lt;br/&amp;gt;cwnd 线性增长]
    B --&amp;gt;|&amp;#34;收到 3 个重复 ACK&amp;#34;| C[快重传&amp;lt;br/&amp;gt;立即重传]
    C --&amp;gt; D[快恢复&amp;lt;br/&amp;gt;cwnd=ssthresh]
    D --&amp;gt; B
    B --&amp;gt;|&amp;#34;超时&amp;#34;| E[超时重传&amp;lt;br/&amp;gt;cwnd=1]
    E --&amp;gt; A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;慢启动（Slow Start）&lt;/strong&gt;：初始窗口小，每收到一个 ACK 窗口近似翻倍，直到达到慢启动阈值（ssthresh）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拥塞避免（Congestion Avoidance）&lt;/strong&gt;：超过阈值后，窗口线性增长。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快重传（Fast Retransmit）&lt;/strong&gt;：收到 3 个重复 ACK 就立刻重传，不等超时。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快恢复（Fast Recovery）&lt;/strong&gt;：快重传后不把窗口降到 1，而是降到 ssthresh，再线性增长。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;67-nagle-算法与延迟-ack&#34;&gt;6.7 Nagle 算法与延迟 ACK&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nagle 算法&lt;/strong&gt;：减少小报文数量。如果上一次发送的数据还没被确认，且当前数据量小于 MSS，就把小数据攒起来一起发。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;延迟 ACK&lt;/strong&gt;：接收方不立即对每个收到的段发 ACK，而是等 40ms 左右，看是否有数据要一起回，减少 ACK 数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nagle + 延迟 ACK 会造成明显延迟&lt;/strong&gt;：发送方等小 ACK，接收方等大数据一起回，形成「写 - 等 - 写 - 等」的 ping-pong 效应。&lt;/li&gt;
&lt;li&gt;交互式应用（如游戏、SSH、RPC）通常要关闭 Nagle：&lt;code&gt;setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &amp;amp;on, sizeof(on))&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;批量传输场景（文件下载）可以保留 Nagle，减少小包数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七dns-与-dhcp网络配置的基础设施&#34;&gt;七、DNS 与 DHCP：网络配置的基础设施&lt;/h2&gt;
&lt;h3 id=&#34;71-dns域名--ip&#34;&gt;7.1 DNS：域名 → IP&lt;/h3&gt;
&lt;p&gt;DNS（Domain Name System）是互联网的「电话簿」，把人类可读的域名解析成 IP 地址。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[客户端] --&amp;gt;|&amp;#34;查询 example.com&amp;#34;| B[本地 DNS 缓存]
    B --&amp;gt;|&amp;#34;未命中&amp;#34;| C[递归 DNS 服务器]
    C --&amp;gt;|&amp;#34;迭代查询&amp;#34;| D[根 DNS 服务器]
    D --&amp;gt; E[.com 顶级域服务器]
    E --&amp;gt; F[example.com 权威服务器]
    F --&amp;gt;|&amp;#34;返回 IP&amp;#34;| C
    C --&amp;gt;|&amp;#34;返回 IP&amp;#34;| A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;DNS 记录类型&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;类型&lt;/th&gt;
					&lt;th&gt;用途&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;A&lt;/td&gt;
					&lt;td&gt;域名 → IPv4 地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;AAAA&lt;/td&gt;
					&lt;td&gt;域名 → IPv6 地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CNAME&lt;/td&gt;
					&lt;td&gt;域名别名&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;MX&lt;/td&gt;
					&lt;td&gt;邮件交换服务器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;NS&lt;/td&gt;
					&lt;td&gt;权威 DNS 服务器&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;TXT&lt;/td&gt;
					&lt;td&gt;文本记录，常用于验证和 SPF&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;避坑技巧&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DNS 有缓存，修改记录后全球生效可能要几分钟到 48 小时（受 TTL 控制）。&lt;/li&gt;
&lt;li&gt;注意 DNS 解析失败和连接超时的区别：先 &lt;code&gt;nslookup&lt;/code&gt;/&lt;code&gt;dig&lt;/code&gt; 确认域名能否解析，再 ping/telnet 确认网络层是否通。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gethostbyname&lt;/code&gt; 不是线程安全的，现代程序应使用 &lt;code&gt;getaddrinfo&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;72-dhcp自动获取网络配置&#34;&gt;7.2 DHCP：自动获取网络配置&lt;/h3&gt;
&lt;p&gt;DHCP（Dynamic Host Configuration Protocol）让主机自动获取 IP 地址、子网掩码、网关、DNS 服务器。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant H as 主机
    participant D as DHCP 服务器

    H-&amp;gt;&amp;gt;D: DHCP Discover（广播）
    D-&amp;gt;&amp;gt;H: DHCP Offer
    H-&amp;gt;&amp;gt;D: DHCP Request
    D-&amp;gt;&amp;gt;H: DHCP ACK&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;八unix-网络编程核心socket-api&#34;&gt;八、Unix 网络编程核心：Socket API&lt;/h2&gt;
&lt;p&gt;Socket 是 Unix 系统提供的网络编程接口，屏蔽了底层协议细节，让程序像读写文件一样通信。&lt;/p&gt;
&lt;h3 id=&#34;81-tcp-服务端典型调用流程&#34;&gt;8.1 TCP 服务端典型调用流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    A[socket] --&amp;gt; B[bind]
    B --&amp;gt; C[listen]
    C --&amp;gt; D[accept]
    D --&amp;gt; E[read/write]
    E --&amp;gt; F[close]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;82-tcp-客户端典型调用流程&#34;&gt;8.2 TCP 客户端典型调用流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    A[socket] --&amp;gt; B[connect]
    B --&amp;gt; C[read/write]
    C --&amp;gt; D[close]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;83-核心-api-说明&#34;&gt;8.3 核心 API 说明&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;API&lt;/th&gt;
					&lt;th&gt;作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;socket()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;创建套接字，指定协议族（IPv4/IPv6）和类型（流式/数据报）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;bind()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;把套接字绑定到本地地址和端口&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;listen()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;把套接字置为监听状态，指定全连接队列长度&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;accept()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;从全连接队列取出一个已完成握手的连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;客户端发起连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;read()/write()&lt;/code&gt; 或 &lt;code&gt;recv()/send()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;收发数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;close()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;关闭连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;shutdown()&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;半关闭：只关闭读或写&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;84-最简-tcp-服务端示例&#34;&gt;8.4 最简 TCP 服务端示例&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;arpa/inet.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define PORT 8080
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define BACKLOG 128
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; listen_fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket&lt;/span&gt;(AF_INET, SOCK_STREAM, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (listen_fd &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;socket&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; reuse &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;setsockopt&lt;/span&gt;(listen_fd, SOL_SOCKET, SO_REUSEADDR, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;reuse, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(reuse));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr_in addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;memset&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;addr, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addr.sin_family &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AF_INET;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addr.sin_addr.s_addr &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; INADDR_ANY;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addr.sin_port &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;htons&lt;/span&gt;(PORT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(listen_fd, (&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;addr, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr)) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bind&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;listen&lt;/span&gt;(listen_fd, BACKLOG) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;listen&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Server listening on port %d&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, PORT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr_in client_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;socklen_t&lt;/span&gt; len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(client_addr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; conn_fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;accept&lt;/span&gt;(listen_fd, (&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;client_addr, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (conn_fd &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;accept&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; buf[&lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;ssize_t&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;read&lt;/span&gt;(conn_fd, buf, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(buf) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            buf[n] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Received: %s&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, buf);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(conn_fd, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;OK&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;(conn_fd);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;(listen_fd);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;85-最简-tcp-客户端示例&#34;&gt;8.5 最简 TCP 客户端示例&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;arpa/inet.h&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define SERVER &amp;#34;127.0.0.1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#define PORT 8080
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket&lt;/span&gt;(AF_INET, SOCK_STREAM, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (fd &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;socket&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr_in addr;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;memset&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;addr, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addr.sin_family &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AF_INET;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    addr.sin_port &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;htons&lt;/span&gt;(PORT);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;inet_pton&lt;/span&gt;(AF_INET, SERVER, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;addr.sin_addr);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;connect&lt;/span&gt;(fd, (&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; sockaddr &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;addr, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(addr)) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;perror&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;connect&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(fd, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; buf[&lt;span style=&#34;color:#ae81ff&#34;&gt;1024&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;ssize_t&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;read&lt;/span&gt;(fd, buf, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(buf) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        buf[n] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;\0&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;printf&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Server reply: %s&amp;#34;&lt;/span&gt;, buf);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;close&lt;/span&gt;(fd);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;九unix-网络编程常见陷阱与避坑技巧&#34;&gt;九、Unix 网络编程常见陷阱与避坑技巧&lt;/h2&gt;
&lt;h3 id=&#34;91-必须设置-so_reuseaddr&#34;&gt;9.1 必须设置 SO_REUSEADDR&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; reuse &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;setsockopt&lt;/span&gt;(listen_fd, SOL_SOCKET, SO_REUSEADDR, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;reuse, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(reuse));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：服务端主动关闭连接后会进入 TIME_WAIT，该连接的本地端口短时间内不能复用。如果不设置 SO_REUSEADDR，重启服务时可能报 &lt;code&gt;Address already in use&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：SO_REUSEADDR 允许复用 TIME_WAIT 的端口，但不会允许两个进程同时绑定到完全相同的 &lt;code&gt;IP:端口&lt;/code&gt;（Linux 上 SO_REUSEPORT 才能做到）。&lt;/p&gt;
&lt;h3 id=&#34;92-正确处理-sigpipe&#34;&gt;9.2 正确处理 SIGPIPE&lt;/h3&gt;
&lt;p&gt;向一个已经收到 RST 的 socket 写数据会触发 &lt;code&gt;SIGPIPE&lt;/code&gt; 信号，默认行为是终止进程。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;signal&lt;/span&gt;(SIGPIPE, SIG_IGN);  &lt;span style=&#34;color:#75715e&#34;&gt;// 忽略信号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;更优雅的做法是在写之前检查连接状态，或者使用 &lt;code&gt;MSG_NOSIGNAL&lt;/code&gt; 标志：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;send&lt;/span&gt;(fd, buf, len, MSG_NOSIGNAL);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;93-处理部分读写&#34;&gt;9.3 处理部分读写&lt;/h3&gt;
&lt;p&gt;TCP 是字节流，&lt;code&gt;read&lt;/code&gt; 和 &lt;code&gt;write&lt;/code&gt; 不保证一次读完或写完。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 可靠的写：循环写直到写完
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ssize_t&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;write_all&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; fd, &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;buf, &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; len) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;char&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;p &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; buf;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;size_t&lt;/span&gt; remaining &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (remaining &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;ssize_t&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(fd, p, remaining);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (n &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (errno &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; EINTR) &lt;span style=&#34;color:#66d9ef&#34;&gt;continue&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        p &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; n;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        remaining &lt;span style=&#34;color:#f92672&#34;&gt;-=&lt;/span&gt; n;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;94-处理-eintr&#34;&gt;9.4 处理 EINTR&lt;/h3&gt;
&lt;p&gt;慢系统调用（如 &lt;code&gt;accept&lt;/code&gt;、&lt;code&gt;read&lt;/code&gt;、&lt;code&gt;write&lt;/code&gt;）被信号中断后会返回 -1 并设置 &lt;code&gt;errno = EINTR&lt;/code&gt;。 robust 的程序应该重试：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; ((n &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;read&lt;/span&gt;(fd, buf, len)) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; errno &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; EINTR)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;95-不要用-strlen-计算二进制数据长度&#34;&gt;9.5 不要用 strlen 计算二进制数据长度&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;send(fd, buf, strlen(buf), 0)&lt;/code&gt; 只适合文本。二进制数据里可能有 &lt;code&gt;\0&lt;/code&gt;，要用实际长度。&lt;/p&gt;
&lt;h3 id=&#34;96-使用-getaddrinfo-替代-gethostbyname&#34;&gt;9.6 使用 getaddrinfo 替代 gethostbyname&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;gethostbyname&lt;/code&gt; 只支持 IPv4，不是线程安全的，已经被废弃。现代程序应使用 &lt;code&gt;getaddrinfo&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; addrinfo hints, &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;res;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;memset&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;hints, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;sizeof&lt;/span&gt;(hints));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hints.ai_family &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; AF_UNSPEC;      &lt;span style=&#34;color:#75715e&#34;&gt;// 支持 IPv4 和 IPv6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hints.ai_socktype &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; SOCK_STREAM;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; err &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getaddrinfo&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;example.com&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;hints, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;res);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (err &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;fprintf&lt;/span&gt;(stderr, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;getaddrinfo: %s&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;gai_strerror&lt;/span&gt;(err));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;exit&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; fd &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;socket&lt;/span&gt;(res&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ai_family, res&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ai_socktype, res&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ai_protocol);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;connect&lt;/span&gt;(fd, res&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ai_addr, res&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ai_addrlen);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;freeaddrinfo&lt;/span&gt;(res);  &lt;span style=&#34;color:#75715e&#34;&gt;// 记得释放
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;97-区分-close-与-shutdown&#34;&gt;9.7 区分 close 与 shutdown&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;close(fd)&lt;/code&gt;：关闭文件描述符，引用计数减到 0 时释放资源，双向关闭。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shutdown(fd, SHUT_WR)&lt;/code&gt;：只关闭写方向，读仍然可以接收数据，常用于通知对端「我发完了」。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;98-阻塞-vs-非阻塞-io&#34;&gt;9.8 阻塞 vs 非阻塞 IO&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;模式&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
					&lt;th&gt;适用场景&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;阻塞&lt;/td&gt;
					&lt;td&gt;调用没完成就挂起&lt;/td&gt;
					&lt;td&gt;简单程序、每个连接一个线程/进程&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;非阻塞&lt;/td&gt;
					&lt;td&gt;调用立即返回，没数据返回 EAGAIN/EWOULDBLOCK&lt;/td&gt;
					&lt;td&gt;高并发、IO 多路复用&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;99-io-多路复用&#34;&gt;9.9 IO 多路复用&lt;/h3&gt;
&lt;p&gt;单线程服务大量连接时，要用 IO 多路复用同时监视多个 fd 的就绪状态。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[主线程] --&amp;gt; B[select/poll/epoll]
    B --&amp;gt;|&amp;#34;哪些 fd 可读/可写&amp;#34;| C[处理可读 fd]
    B --&amp;gt; D[处理可写 fd]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;机制&lt;/th&gt;
					&lt;th&gt;特点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;select&lt;/td&gt;
					&lt;td&gt;有 fd 数量限制（通常 1024），每次调用要遍历全部 fd&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;poll&lt;/td&gt;
					&lt;td&gt;用数组描述 fd，没有数量硬限制，但仍需遍历&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;epoll（Linux）&lt;/td&gt;
					&lt;td&gt;事件驱动，只关注活跃的 fd，支持边缘触发和水平触发&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;kqueue（BSD/macOS）&lt;/td&gt;
					&lt;td&gt;类似 epoll，事件驱动&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;epoll 水平触发（LT）vs 边缘触发（ET）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LT：只要 fd 还有数据可读，epoll 就会一直通知。&lt;/li&gt;
&lt;li&gt;ET：只有 fd 状态变化时通知一次，需要一次性把数据读完，通常配合非阻塞 IO。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十典型-unix-网络障碍问题与排查思路&#34;&gt;十、典型 Unix 网络障碍问题与排查思路&lt;/h2&gt;
&lt;h3 id=&#34;101-排查流程总览&#34;&gt;10.1 排查流程总览&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[网络问题] --&amp;gt; B[确认是 DNS/路由/端口/应用哪一层]
    B --&amp;gt; C{能解析域名吗?}
    C --&amp;gt;|否| D[检查 DNS / hosts / 防火墙]
    C --&amp;gt;|是| E{能 ping 通 IP 吗?}
    E --&amp;gt;|否| F[检查路由 / ACL / 安全组]
    E --&amp;gt;|是| G{端口通吗?}
    G --&amp;gt;|否| H[检查服务是否监听 / 防火墙 / 安全组]
    G --&amp;gt;|是| I[检查应用日志 / 抓包 / 连接状态]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;102-connection-refused&#34;&gt;10.2 Connection refused&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：目标 IP 可达，但目标端口没有进程在监听，或者防火墙明确拒绝。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 服务端检查是否在监听&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ss -tlnp | grep &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;netstat -tlnp | grep &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 客户端测试端口是否开放&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;telnet server_ip &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;nc -vz server_ip &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;103-connection-timed-out&#34;&gt;10.3 Connection timed out&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：报文到达目的地过程中被丢弃，没有收到任何 RST 或响应，常见于防火墙静默丢弃、路由不可达、NAT 映射问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;排查&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 看路径到哪里断了&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;traceroute server_ip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mtr server_ip
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 抓包看 SYN 有没有出去、有没有回来&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;tcpdump -i any port &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&lt;/span&gt; -nn&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;104-too-many-time_wait--端口耗尽&#34;&gt;10.4 Too many TIME_WAIT / 端口耗尽&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：高并发短连接场景下，客户端连接失败，报错 &lt;code&gt;Cannot assign requested address&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：大量主动关闭的连接堆积在 TIME_WAIT，占用了本地端口。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决思路&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;尽量使用长连接 / 连接池。&lt;/li&gt;
&lt;li&gt;服务端设置 &lt;code&gt;SO_REUSEADDR&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Linux 可调整 &lt;code&gt;net.ipv4.ip_local_port_range&lt;/code&gt; 扩大临时端口范围。&lt;/li&gt;
&lt;li&gt;开启 &lt;code&gt;net.ipv4.tcp_tw_reuse&lt;/code&gt;（谨慎，仅对客户端出站连接有效，不要开 &lt;code&gt;tcp_tw_recycle&lt;/code&gt;，已在内核 4.12 后移除）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;105-close_wait-堆积&#34;&gt;10.5 CLOSE_WAIT 堆积&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：服务端 &lt;code&gt;ss -tan | grep CLOSE_WAIT&lt;/code&gt; 看到很多连接。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：对端已经发了 FIN，但本端应用没有调用 &lt;code&gt;close()&lt;/code&gt;，通常是因为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;应用逻辑忘记关闭 socket。&lt;/li&gt;
&lt;li&gt;应用在处理完数据前阻塞在某处，无法执行 close。&lt;/li&gt;
&lt;li&gt;异常分支没有释放连接资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：检查代码里所有异常路径是否都调用了 &lt;code&gt;close&lt;/code&gt;，确保每个 &lt;code&gt;accept&lt;/code&gt; 出来的 fd 都有对应的关闭点。&lt;/p&gt;
&lt;h3 id=&#34;106-syn-flood--半连接队列溢出&#34;&gt;10.6 SYN Flood / 半连接队列溢出&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：服务偶尔出现连接建立慢或大量 &lt;code&gt;SYN_SENT&lt;/code&gt;/&lt;code&gt;SYN_RECV&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：Linux 内核有两个队列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;半连接队列（syn queue）&lt;/strong&gt;：存放收到 SYN 但还没完成三次握手的连接。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全连接队列（accept queue）&lt;/strong&gt;：存放已完成三次握手但还没被 &lt;code&gt;accept&lt;/code&gt; 取走的连接。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[客户端 SYN] --&amp;gt;|&amp;#34;进入半连接队列&amp;#34;| B[SYN_RCVD]
    B --&amp;gt;|&amp;#34;收到 ACK&amp;#34;| C[ESTABLISHED]
    C --&amp;gt;|&amp;#34;进入全连接队列&amp;#34;| D[等待 accept]
    D --&amp;gt;|&amp;#34;accept 取走&amp;#34;| E[交给业务处理]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;排查&lt;/strong&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ss -s          &lt;span style=&#34;color:#75715e&#34;&gt;# 查看连接统计&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ss -lnt        &lt;span style=&#34;color:#75715e&#34;&gt;# 看全连接队列长度和溢出次数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;netstat -s | grep overflow&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;调大 &lt;code&gt;net.ipv4.tcp_max_syn_backlog&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;开启 SYN Cookies：&lt;code&gt;net.ipv4.tcp_syncookies = 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;增大 &lt;code&gt;listen(fd, backlog)&lt;/code&gt; 的 backlog，同时配合 &lt;code&gt;somaxconn&lt;/code&gt; 调整。&lt;/li&gt;
&lt;li&gt;提升应用 &lt;code&gt;accept&lt;/code&gt; 速度。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;107-高并发下-accept-慢或全连接队列溢出&#34;&gt;10.7 高并发下 accept 慢或全连接队列溢出&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：连接完成三次握手，但客户端一直等待服务端响应。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：&lt;code&gt;accept&lt;/code&gt; 处理太慢，全连接队列满了，新的完成连接被丢弃。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用多线程/多进程分担 accept。&lt;/li&gt;
&lt;li&gt;使用 IO 多路复用 + 事件驱动架构。&lt;/li&gt;
&lt;li&gt;调整 &lt;code&gt;net.core.somaxconn&lt;/code&gt; 和 &lt;code&gt;listen()&lt;/code&gt; 的 backlog。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;108-mtumss-导致的大包不通&#34;&gt;10.8 MTU/MSS 导致的大包不通&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：小数据包能通，大数据包不通；或某些网站/服务能打开，某些打不开。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：路径中某段链路 MTU 较小，大包被丢弃，且中间设备不发送 ICMP &amp;ldquo;Fragmentation Needed&amp;rdquo;，导致无法自动降包大小。这就是著名的 &lt;strong&gt;PMTUD（Path MTU Discovery）黑洞&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;设置 &lt;code&gt;net.ipv4.tcp_mtu_probing = 1&lt;/code&gt; 启用 TCP MTU 探测。&lt;/li&gt;
&lt;li&gt;在 VPN/隧道两端统一调低 MTU/MSS。&lt;/li&gt;
&lt;li&gt;中间网络设备不要丢弃 ICMP &amp;ldquo;Fragmentation Needed&amp;rdquo;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;109-应用层协议粘包拆包&#34;&gt;10.9 应用层协议粘包/拆包&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：TCP 客户端发了两条消息，服务端一次 read 读到了两条消息的内容。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：TCP 是字节流，没有消息边界。应用层需要自己定义消息边界。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见解决方案&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;固定长度&lt;/strong&gt;：每个消息固定 N 字节。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分隔符&lt;/strong&gt;：如 &lt;code&gt;\n&lt;/code&gt;、自定义分隔符。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长度前缀&lt;/strong&gt;：消息头 4 字节表示消息体长度。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// 长度前缀示例：先读 4 字节长度，再读对应长度的 body
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;read_all&lt;/span&gt;(fd, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;len, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;len &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ntohl&lt;/span&gt;(len);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;read_all&lt;/span&gt;(fd, body, len);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;十一学习路线与推荐资源&#34;&gt;十一、学习路线与推荐资源&lt;/h2&gt;
&lt;h3 id=&#34;111-学习顺序建议&#34;&gt;11.1 学习顺序建议&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[网络分层模型] --&amp;gt; B[IP/子网/路由]
    B --&amp;gt; C[ARP/ICMP]
    C --&amp;gt; D[TCP 握手挥手与状态机]
    D --&amp;gt; E[滑动窗口与拥塞控制]
    E --&amp;gt; F[Socket API 编程]
    F --&amp;gt; G[IO 多路复用]
    G --&amp;gt; H[Wireshark 抓包分析]
    H --&amp;gt; I[线上问题排查]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;112-推荐实践&#34;&gt;11.2 推荐实践&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;用 Wireshark 抓一次完整的三次握手和四次挥手&lt;/strong&gt;，亲眼看看每个字段的值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;写一个 echo 服务器&lt;/strong&gt;，先用阻塞 IO，再用 select/epoll 改写。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;故意制造问题&lt;/strong&gt;：把客户端改成快速创建大量短连接，观察 TIME_WAIT；把服务端 accept 故意睡几秒，观察全连接队列溢出。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用 &lt;code&gt;ss&lt;/code&gt;、&lt;code&gt;tcpdump&lt;/code&gt;、&lt;code&gt;traceroute&lt;/code&gt;、&lt;code&gt;nc&lt;/code&gt;、&lt;code&gt;iperf&lt;/code&gt; 这些工具做实验&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;113-推荐参考书&#34;&gt;11.3 推荐参考书&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;《TCP/IP 详解 卷1：协议》（W. Richard Stevens）——协议细节最权威。&lt;/li&gt;
&lt;li&gt;《Unix 网络编程 卷1：套接字联网 API》（W. Richard Stevens）——Socket 编程圣经。&lt;/li&gt;
&lt;li&gt;《Wireshark 网络分析就这么简单》——抓包实战。&lt;/li&gt;
&lt;li&gt;《Linux 高性能服务器编程》（游双）——中文入门友好。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十二总结&#34;&gt;十二、总结&lt;/h2&gt;
&lt;p&gt;TCP/IP 协议栈并不神秘，它的核心思想就是&lt;strong&gt;分层与解耦&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;链路层负责「相邻设备怎么传」。&lt;/li&gt;
&lt;li&gt;网络层负责「怎么找到对方主机」。&lt;/li&gt;
&lt;li&gt;传输层负责「怎么把数据交给正确的进程，并决定可靠还是高效」。&lt;/li&gt;
&lt;li&gt;应用层负责「这些数据代表什么业务含义」。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unix 网络编程的核心是理解 Socket API 的调用顺序、IO 模型、以及 TCP 连接的生命周期。写出稳定的网络程序，不在于记住所有参数，而在于理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;连接是什么时候建立的，什么时候关闭的。&lt;/li&gt;
&lt;li&gt;数据是怎么流动的，会不会被截断、会不会粘包。&lt;/li&gt;
&lt;li&gt;异常情况下系统会怎么表现，程序能不能正确处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;希望这份总结能帮你把《TCP/IP 详解》里的知识点串成一张网，少踩一些网络编程里的经典大坑。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
