📚 第一章 网络基础知识
📄 一句话概括

从「一台机器分给多人」到「多台机器互相说话」,多了一样东西:地址。 这一节把这句话量出来:一次 TCP 握手在本机到底要多久。

📝 本节和前面几节的关系

1.2.2 的分时系统解决的是「一台机器怎么分」,这一节解决「多台机器怎么连」。 1.2.4 会讲这件事怎么变成「计算机网络」这个学科。

一、多出来的是「寻址」

一台机器内部通信,两个进程靠进程号或端口就能找到对方。 跨机器就不行了——得先找到机器,再找到机器上的进程。

范围 怎么找对方 需要什么
同一台机器 进程号/端口 操作系统内部表
同一个网段 MAC 地址 ARP 广播
跨网段 IP 地址 路由表

第三行那个「路由表」是网络真正诞生的标志: 有了它,两台机器不需要直连也能通信——中间可以隔任意多台设备。 这也是「互联网」这个词的字面意思:网络之间互连(inter-network)。

二、一次握手到底多久

TCP 建立连接要三次握手,这是所有网络编程的起点。 它的耗时由两段组成:

组成 来源 量级
传播时延 信号在介质里跑 回环≈0,同城 1 毫秒,跨洋百毫秒
处理时延 协议栈拼包解包 微秒到毫秒
排队时延 设备忙时等 不可预测

第一行那个「回环≈0」很重要:回环上测到的就全是处理时延, 所以它只能说明协议栈开销,不能代表网络。 真要测网络,必须跨机器——而跨机器的数受环境影响极大,没法当基准。 这是网络性能测试的根本困难:你测到的永远是你那一段路,不是全路。

三、为什么不用「文件共享」代替

早期确实有人这么干:把文件放在共享目录,别人来读。但它有三个绕不过去的短板:

短板 表现 通信方式怎么解决
实时性 文件有轮询间隔 长连接推送
并发 同时写会冲突 协议层加锁与序号
异构 不同系统路径规则不同 统一的报文格式

第二行那个「协议层加锁」就是 TCP 序号机制的由来:它保证两端的字节流顺序一致、不重不漏。 所以「计算机之间的通信」不是「文件传输的升级版」,它是一套新的抽象: 把「一堆机器」看成「一张能寻址的网」。 这个抽象一旦成立,上层就不用关心底下有几台设备、走的什么线。

四、动手 Demo:两台计算机通信,一次来回要多久

起一个监听端口,真连上去 30 次,量 TCP 三次握手的耗时。 先预热一次再计时,把第一次的冷启动开销丢掉。 这个 Demo 测的是回环,也就是协议栈本身的开销。

import socket, threading
import _netsim as N

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.bind(('127.0.0.1', 0))
srv.listen(8)
port = srv.getsockname()[1]

def acc():
    while True:
        try:
            c, _ = srv.accept()
        except OSError:
            return
        c.close()

threading.Thread(target=acc, daemon=True).start()
N.tcp_connect_ms('127.0.0.1', port)     # 预热,丢掉
res = sorted(N.tcp_connect_ms('127.0.0.1', port)[0] for _ in range(30))
print('最小 %.3f  中位 %.3f  最大 %.3f 毫秒'
      % (res[0], res[len(res) // 2], res[-1]))
print('最大/最小 %.1f 倍' % (res[-1] / res[0]))
srv.close()

实跑输出:

--------------------------------------------------------------------
两台计算机通信,一次来回要多久
--------------------------------------------------------------------
**「计算机之间的通信」听起来抽象,量一下就很具体**:起一个监听端口,真的连上去,
量 TCP 三次握手用了多少时间。

  项目           数值
  ---------------------------
  次数           30
  最小           0.282 毫秒
  中位           14.465 毫秒
  最大           15.845 毫秒
  最大/最小     56.1 倍

**注意这是回环地址**:数据根本没出网卡,全程在操作系统内部走了一圈。
**所以这个数只说明「协议栈本身的往返开销」,不代表真实网络。**

**但最大与最小差了 56 倍**——回环上都能差这么多,说明单次计时根本没有意义。
**所以看网络性能只看两样:中位数与吞吐;最小值和最大值都会骗人。**

**真实网络里,同样一次握手要加上两段链路传播时延**:同城一般 1 毫秒上下,跨省十几毫秒,
跨国上百毫秒。**这些数本机测不到,不给估计值。**

最大与最小差几十倍,这不是测错了,是回环本身就抖。 原因在于「什么时候被调度」:计时的那一下可能正好赶上系统在忙别的事。 所以网络性能只看两样:中位数(稳不稳定)与吞吐(能跑多快)。 最小值和最大值都会骗人。 这一点在跨机器测量里更明显:同一段路,早上和晚上的时延能差好几倍。

30 秒实验

  1. 把 30 次改成 300 次,看中位数会不会变——中位数应该比较稳,最大值会更大。
  2. 把 127.0.0.1 换成你本机的局域网 IP(如 192.168.3.152),看中位数变不变。
  3. 把端口改成没有服务监听的一个,看错误类型与耗时有什么不同。

常见误区

  1. 以为回环测出来的就是网络时延。 回环不碰网卡,测到的全是协议栈开销。 真实网络还要加上传播与排队。
  2. 以为最小耗时代表性能。 最小值往往是个偶然,中位数才代表常态。
  3. 以为三次握手很贵。 在回环上它是微秒到毫秒级, 真正贵的是跨洋的传播时延——那是物理定律,优化不掉。
自测三问(点开对答案) 1. 同一台机器内的通信与跨机器通信,最本质的区别是什么? —— **多了一层「寻址」**:机器内靠端口,跨机器还要先找到机器。 **跨网段时靠路由表**,这才是「网络」成立的前提。 2. 回环上测到的握手耗时能不能代表真实网络? —— **不能**。回环不经过网卡与链路,测到的只是协议栈处理开销。**真实网络还要加传播、排队时延。** 3. 为什么说网络性能只看中位数与吞吐? —— **最小值是偶然,最大值受系统负载影响**,两者都不代表常态。 **中位数反映稳定水平,吞吐反映上限。**

下一节把这件事变成一个学科:当越来越多的机器连起来,地址该怎么分配?

相关笔记