🌐 第三章 网络间的通用语言TCP/IP
📄 一句话概括

前四节把四层和各层职责都拆完了,这一节把它们拼起来跑一遍真实通信:开口前先打招呼(三次握手建连接)→ 说一句要对方应一声(每块数据都要确认)→ 对方没应声就重说一遍(超时重传)→ 挂断也要两边都道别(四次挥手)。一句话记住:TCP/IP 通信 = 先约会、再对账、最后道别。

一、全景:一次通信从头到尾

把 3.1~3.4 拼成一条时间线:

① 应用层(3.3)    浏览器说:"我要这个网页"
② 传输层(3.3)    先建连接(本节重点)→ 把请求切成块、编号
③ 网际层(3.2)    每块加 IP 头,写上源/目标门牌、交给下一跳
④ 网络接口层(3.2)加帧头,变成电信号/光信号上路
⑤ 线路上(1.4)    逐跳接力,一格一格交棒到对端
⑥ 接收方(3.4)    自下而上逐层脱信封,应用层拿到请求
⑦ 服务器生成响应,反过来再走一遍 ②~⑤
⑧ 传输完毕        关闭连接(本节的最后一节)

本节专门看 ②怎么开、③之前怎么确认对方真收到了、⑧怎么关——也就是标题说的"接收和传输数据的规则"。

二、开口前:三次握手(先把手搭上)

TCP 不是"拿起来就说",而是先建立连接。建立的过程固定三步:

次序 谁发 报文 说的是什么
① 客户端 SYN "我要连你,我的起始序号是 X"
② 服务器 SYN + ACK "收到,同意;我的起始序号是 Y"
③ 客户端 ACK "收到你的序号了,开始吧"

为什么两次不够?

生活类比:打电话的前三句——"喂,听得到吗?"→"听得到,你听得到我吗?"→"听得到,开始吧"。

💡 握手的第二个作用:交换起始序号

三次握手顺便把初始序号报给对方。没有序号,后面"确认 + 重传"那套对账全无从谈起(序号就是 3.4 包头上那个"第几块")。

三、传输中:发一块,确认一块(本节核心)

连接建好,开始真正传数据。规矩就四条:

  1. 编号——每个块都有序号,顺序清清楚楚(收 3.4 的"第几块")
  2. 应答(ACK)——接收方每收到一块,就回一句"第 N 块我收到了"
  3. 超时重传——发送方发出去就开始计时,到点还没等到确认,就当它丢了,重发这一块
  4. 按序重组——就算 3 号先到、2 号后到,也按序号排整齐再交给程序

为什么这套规矩值钱?

💡 收 Java 13.2 的旧账

那时在 Socket 里 os.write(...) 写完就以为对方收到了。真相是:底下 TCP 正在默默做"确认 + 重传",你在 Java 里一个字都看不见。上层能拿到一根可靠的管子,是因为下层一直在对账。

四、接收方也有规矩:窗口与流量控制

收发是双方的事,规则不只约束发送方:

💡 由此得到一个重要结论

互联网两端的速度、带宽、线路质量千差万别,TCP 靠"确认 + 窗口"动态调整自己的发送节奏,不需要谁去统一配置。这就是 3.1 说的"通用语言"落到实处:规矩一样,节奏各调各的。

五、收尾:四次挥手(道别也要两边都说完)

聊完了要断开,TCP 的断开也是固定流程:

客户端 ── FIN ──────→ 服务器     "我说完了"
客户端 ←── ACK ────── 服务器     "知道了"
客户端 ←── FIN ────── 服务器     "我也说完了"
客户端 ── ACK ──────→ 服务器     "知道了"

为什么握手是三次、挥手却是四次?

因为 TCP 是全双工的——两个方向各有一条独立通道,握手时服务器把"同意"和"我的序号"合并在一个报文里(SYN + ACK 合体),而关闭时每个方向都要各自关一次,没法合并,所以是 4 次。

生活类比:挂电话时不能自己说一句就按掉——要等对方也说完,不然就像电话那头明明还有人讲话,你这边已经挂线了。

六、动手 Demo19:亲眼看着连接的一生

  1. 打开命令行(Win+R → cmd)

  2. 先记一个底数: netstat -an | findstr ESTABLISHED

  3. 列出当前所有"已接通"的连接(3.3 的 Demo17 见过它)

  4. 打开浏览器访问一个网站,再跑一遍

  5. 会多出若干条到 :443(加密网页的知名端口,3.3 的老朋友)的 ESTABLISHED
  6. 每一条都是一次三次握手换来的

  7. 再看连接怎么消失:关掉浏览器标签,马上再跑一遍

  8. 有些条目会变成 TIME_WAIT —— 意思是"我已经挥手道别了,还在原地等一会儿"
  9. 等这个状态是为了确保对方收到最后的确认,不是故障

  10. 看一次完整的请求与应答(Win10 1803 以上自带 curl): curl -v https://www.baidu.com

  11. -v 是详细模式:能看到 Connected to ...(连接建好了)、请求头、响应头、状态码
  12. 这正是 1.6 里 F12 看到的"应用层对话",只不过这次是在命令行里裸看

  13. 30 秒观察:反复刷新同一个网页,TIME_WAIT 的条数会往上堆——这不是故障,是正常道别的痕迹。攒到成千上万才需要调优(把那台机器同时能用的连接号配额耗光了)。

❓ 自测一下
  1. 三次握手的三个报文分别是什么?为什么两次不够?
  2. TCP 怎么知道某一块数据丢了?丢了以后怎么办?
  3. 接收方的"窗口"是干什么用的?
  4. 建立连接是三次、断开是四次,差异从哪来?

答案: ① SYN、SYN+ACK、ACK。两次的话服务器无法确认"客户端那条方向也能收",三次才能让双方都确认收发双向通畅。 ② 发出去就开始计时,超时没等到 ACK 就判定丢了,重传这一块;接收方按序号重组。核心思想:没有确认 = 没送到。 ③ 告诉发送方"我还能接多少字节",防止接收方缓冲区被撑爆(流量控制),快的不能欺负慢的。 ④ 因为 TCP 是全双工,两个方向各自独立关闭,每个方向都要"我说完了 + 你收到"两下;握手时"同意"和"我的序号"能合并成一个报文(SYN+ACK),所以少一次。

常见误区:

  1. ❌ "发出去就等于送到了" → 在 TCP 里,没有确认就不算数
  2. ❌ "重传是路由器或网际层负责的" → IP 只尽力而为(3.2 就说了"丢了不补"),重传是 TCP 的活
  3. ❌ "三次握手是为了加密安全" → 握手只解决"双方都在 + 序号对上";加密是 TLS 的活(1.6 的分层:各管一段,别混为一谈)
  4. ❌ "UDP 也得握手" → UDP 没有连接的概念,打包就扔(3.3),所以快,但没人对账
  5. ❌ "TIME_WAIT 是连接没关干净、是故障" → 它是正常的道别收尾状态,等一会儿才消失

七、下一步学什么

相关笔记