本文是对 A short (and mostly wrong) history of computer networking 的整理与翻译。
内容结构概览
- 为什么要先讲网络史:实现 ping 之前,需要先知道计算机之间如何通信。
- 为什么要连接计算机:从早期大型机、打孔卡、分时系统、终端讲起。
- 远程终端不等于计算机网络:终端只是访问大型机,不是多台计算机互联。
- ARPANET、Telnet 与 TCP/IP:现代互联网的雏形来自数据交换和远程控制需求。
- 两台计算机如何通信:电缆、电信号、比特、模拟信号、噪声与时钟同步。
- 多台计算机为什么不能全连接:网卡数量、线缆复杂度和扩展性都会爆炸。
- 总线与以太网思想:共享介质、数据报、MAC 地址、冲突检测、随机等待、校验和。
- 为什么还需要 IP:MAC 地址适合局域网识别,IP 地址适合跨网络路由。
- DNS 与 DHCP:域名解决记不住 IP 的问题,DHCP 解决手动配置 IP 的问题。
- 回到 ping:ping 是理解网络协议栈的入口,不只是一个测试连通性的命令。
如果想自己实现一个ping,很多人第一反应是去查 ICMP 协议格式,然后直接写代码构造数据包。但如果一开始就这么做,很容易只学到“怎么填字段”,却没有真正理解这些字段为什么存在,也不知道一个看似简单的ping命令背后到底经历了哪些网络层次。
ping表面上很简单:输入一个 IP 地址,程序发出请求,目标机器如果能收到并返回响应,屏幕上就显示延迟、丢包率等信息。但真正发生的事情远比这复杂。你的机器要知道目标地址是不是本地网络,要不要交给网关;如果要发到局域网内,还要知道对方的 MAC 地址;如果目标在公网,还要经过一系列路由器转发;如果你输入的是域名,还要先通过 DNS 查出 IP;如果机器刚接入网络,自己的 IP、网关和 DNS 也可能是通过 DHCP 自动获得的。
所以,在自己实现ping之前,先把计算机网络这件事讲明白是有必要的。网络不是一套凭空设计出来的复杂名词,而是人们在解决一个又一个具体问题时逐步形成的系统。先有两台计算机之间传递数据的需求,再有多台计算机共享通信介质的问题;先有局域网内部寻址,再有跨网络路由;先有 IP 地址,再有人类更容易记住的域名;最后,才有我们每天随手使用的ping、浏览器、SSH、Git、数据库连接和各种网络服务。
一、计算机为什么需要联网
今天我们几乎默认计算机一定要联网。手机、电脑、服务器、路由器、摄像头、智能家居设备都在网络里,很多软件如果没有网络就近乎不可用。但在早期,计算机并不是这样工作的。20 世纪 40 年代的计算机体积巨大、价格昂贵,能负担得起计算机的机构通常也只有一台大型机器,而且这台机器一次只能执行一个任务。
以 ENIAC 这类早期计算机为例,使用方式和今天完全不同。那时所谓“编程”,往往意味着拨动开关、插拔电缆,再通过打孔卡输入数据。输出结果也可以通过打印设备写回打孔卡。站在非常宽泛的角度看,打孔卡也算一种让计算机和外部世界交换数据的方式,但它显然还不是我们今天理解的计算机网络。
真正的变化来自资源共享。早期计算机太贵,不可能每个人都有一台,所以人们开始考虑怎样让多人共享同一台大型计算机。分时系统就是这种思路下的产物。最开始,系统可能只是把用户提交的任务排队执行,一个任务结束后马上运行下一个任务。后来,计算机可以把任务切成更小的时间片,快速在多个任务之间切换。虽然硬件在某个瞬间仍然只做一件事,但用户感受到的是多个任务似乎在同时运行,这就是多任务的早期形态。
用户和大型机交互时,使用的是终端。终端通常有键盘和显示器,但它本身不是完整的计算机,不负责真正的计算。终端更像是大型机的远程输入输出设备:用户通过键盘输入命令,命令传到大型机执行,结果再显示在终端屏幕上。某些高级分时系统已经允许终端远程接入,这在形式上很接近今天的远程访问,但它仍然不是多台独立计算机之间的通信,因为终端本身并不承担计算任务。
与此同时,另一条路线也在发展:让不同的计算机互相连接。这里的目标不只是让人远程使用一台大型机,而是让多台计算机之间交换数据。不同机器可以共享信息,各自执行擅长的任务,再组合起来解决更复杂的问题。ARPANET 就属于这个方向,早期计划在 1967 年左右已经出现。它的重点是数据交换,而不是简单地把某台机器的操作界面搬到远处。
后来,远程控制和数据交换这两类需求逐渐融合。Telnet 这样的远程登录协议出现后,人们可以通过网络控制另一台计算机。Telnet 最早运行在 ARPANET 的 NCP 之上,后来在 1983 年迁移到 TCP/IP。这次切换通常被视为现代互联网形成过程中的重要节点。到这里,计算机网络的核心动机已经很清楚:昂贵的计算能力需要共享,集中保存的信息需要访问,不同地区的人和组织需要高效通信,而技术本身也会推动更多连接方式出现。
二、两台计算机怎样连接
先从最简单的情况开始:只有两台计算机。如果只想让两台机器通信,最直接的办法就是用一根线把它们连起来。计算机是电子设备,电缆可以传输电信号,只要线足够短、导电性足够好、信号损耗可以忽略,就可以先假设一台计算机能把电信号传到另一台计算机。
但计算机内部处理的是比特,也就是 0 和 1;电信号本身不是天然的 0 和 1,它是连续变化的模拟量。因此,还需要一套编码方式,把比特转换成电信号,再在接收端把电信号还原成比特。比如,可以约定每隔 100 纳秒检测一次电压变化,通过信号穿过 0V 的方向来判断发送的是 0 还是 1。这个例子只是为了说明思路,真实世界中的信号编码、同步、抗干扰会复杂得多。
只要电缆足够长,问题就会变多。信号在传输中会衰减,周围电气设备会带来噪声,发送端和接收端的时钟也不可能天然完全一致。要想稳定传输数据,就必须处理信号质量、同步、错误检测等问题。这些属于物理层和电气工程领域,细节可以非常复杂,但在理解网络整体结构时,可以先做一个抽象:假设我们已经有办法在两台计算机之间可靠地发送 0 和 1。
有了这个抽象,后面的网络协议就可以搭建起来。协议的本质不是从零创造通信能力,而是在某种已有传输能力之上规定数据格式、地址、顺序、错误处理和转发规则。只要最底层能把比特从一端送到另一端,就可以逐步构建出越来越复杂的通信系统。
三、三台、六台、更多台计算机怎么办
两台计算机用一根线连接很容易理解。如果有三台计算机,最直接的办法是每两台之间都拉一根线。这样 A 和 B 之间有一条链路,A 和 C 之间有一条链路,B 和 C 之间也有一条链路。每台机器可以配多个网络接口,每个接口连接不同机器。收到某个接口传来的数据时,就能判断数据来自哪台机器。
三台机器时,这种结构还勉强可以接受。但机器数量一多,线缆和网卡数量就会迅速爆炸。六台计算机之间如果要两两直接连接,每台机器就需要五个网络接口,整个环境里会出现大量交叉线缆。逻辑图看起来已经很混乱,真正放到建筑物里布线只会更麻烦。新增一台机器也不是简单插上一根线,而是要和已有机器分别连接,扩展成本很高。
这说明全连接结构不适合规模化。它在数学上很好理解,但在工程上很快就变得昂贵、复杂、难维护。要想连接更多计算机,必须换一种思路:不要让每两台机器都拥有独立线路,而是让多台机器共享某种通信介质。
这就引出了总线结构。总线可以理解成一根主干电缆,所有计算机都接到这根电缆上。每台计算机只需要一个网络接口,通过一根线接入这条公共总线。这样一来,线缆数量大幅减少,每台机器也不需要为其他每台机器单独准备网卡。
但总线虽然解决了布线问题,却马上带来了新的问题。所有机器都在同一根线上通信,大家会互相听到对方的信号;如果没有地址,就不知道数据是发给谁的;如果没有规则,多台机器还可能同时发送数据,导致信号互相干扰。连接方式变简单了,通信规则反而必须变复杂。
四、共享一根线之后,必须有数据片段和地址
在两台计算机直接相连时,一台机器可以一直发送,因为这条链路只属于它们两个。接收方天然知道数据来自谁,也知道数据是发给自己的。但在总线结构里,所有机器共享同一根通信介质,任何一台机器发送的数据都会出现在总线上,其他机器都有机会听到。
这时,通信不能再是一段无限持续的“讲话”,而要被切成一个个片段。每个片段可以理解成一次较小的数据传输,也就是后面各种网络协议中常见的帧、包、数据报等概念。共享介质在任意时刻只能承载有限通信,但如果把多个对话切成许多小片段,并在时间上快速交替发送,就能制造出多组通信似乎同时进行的效果。这和分时系统有类似思想:用快速切换来共享唯一资源。
解决“谁是谁”的问题,则需要地址。更准确地说,需要给每个网络接口分配一个唯一地址,而不是简单给每台计算机分配一个地址。因为一台计算机可以有多个网络接口,每个接口可能连接不同网络。这个地址就是 MAC 地址的思想。MAC 是 Media Access Control,可以理解为在共享通信介质上识别网络接口、控制访问的一种地址。
如果 MAC 地址用 6 个字节表示,就能提供大约 281 万亿个不同地址。这个空间足够大,可以给非常多的网络接口分配唯一标识。有了 MAC 地址之后,每个数据片段里就可以带上源 MAC 地址和目标 MAC 地址。源地址说明这段数据是谁发出的,目标地址说明这段数据希望交给谁。总线上的每台机器都能看到数据,但只有目标 MAC 匹配自己的机器才需要处理,其他机器可以忽略。
这就从根本上改变了共享介质的通信方式。以前一根线只连接两台机器,不需要在数据里写明身份;现在一根线连接多台机器,身份必须成为数据格式的一部分。地址不是额外装饰,而是共享网络能正常工作的前提。
五、大家同时发送怎么办
共享总线还要解决冲突问题。如果两台机器同时往总线上发送电信号,信号会互相叠加、干扰,接收方就无法正确还原数据。数据片段会损坏,通信也就失败了。既然所有机器都连在同一根总线上,它们也能听到总线上是否有其他机器正在发送信号。一个自然的规则是:发送之前先监听,如果总线上有信号,就先等待;等总线安静下来,再开始发送。
这个规则解决了一部分问题,但还不够。如果多台机器都在等待总线空闲,它们可能同时检测到“现在安静了”,然后一起开始发送,于是又发生冲突。为了降低这种情况,可以让机器在发生冲突后等待一段随机时间,再重新尝试发送。随机等待可以避免所有机器总是在同一时刻重试,从而减少反复冲突的概率。
即使有监听和随机等待,传输错误仍然不可避免。电气噪声、冲突、信号衰减都可能破坏数据。因此,每个数据片段还可以附带校验和。接收方根据校验和判断数据是否损坏。如果校验失败,就丢弃这段数据。至于是否重传、什么时候重传,可以交给更高层协议处理。
到这里,一套局域网通信的基本模型已经出现:底层负责把比特变成信号并发出去;数据链路层负责把连续通信切成片段,给片段加上源地址、目标地址和校验信息;所有机器共享通信介质,但通过监听、冲突处理和随机等待减少互相干扰。这套思想就是以太网的重要基础。
以太网不仅是“把线插上”这么简单,它同时包含物理层和数据链路层的设计。物理层关心怎样在线缆上传输比特,数据链路层关心在共享介质中怎样识别设备、怎样组织数据、怎样发现错误、怎样处理冲突。现代以太网已经和早期共享总线形态有很大不同,但理解总线模型有助于理解 MAC 地址和数据链路层为什么存在。
六、局域网不能无限扩展
总线结构和以太网思路解决了一个局部问题:在一个相对有限的范围内,多台计算机可以共享通信介质并互相识别。但它不能无限扩展。想象把整个欧洲都接到一根超长电缆上,让所有计算机都处在同一个总线网络里,这在工程上几乎不可行。
首先,线缆越长,信号衰减和失真越严重。电信号通过金属导体传播时不可能完全保持原样,距离越远,噪声、衰减和延迟越明显。到了一定距离后,接收端看到的信号可能已经很难和噪声区分。
即使假设有一种理想导体,完全没有能量损耗,仍然绕不过传播速度限制。信号传播速度最多也只能接近光速,而光速虽然很快,却不是无限快。如果马德里到伦敦相距一千多公里,信号从一端传到另一端也需要数毫秒。对于人来说,几毫秒很短;对于 100Mbit/s 这种传输速率来说,几毫秒足够连续发送大量比特。
这会破坏前面依赖“监听总线”的冲突检测策略。因为远处某台机器开始发送时,信号还没有传到你这里,你可能以为总线是空闲的,于是也开始发送。等双方发现冲突时,已经发出了大量数据。距离越远,这种问题越严重。因此,共享总线不适合构建跨城市、跨国家、跨洲的巨大网络。
于是需要另一类设备来连接不同网络。最简单的想法是做一个有多个以太网端口的机器,从某个端口收到数据后,就把它原样转发到其他端口。这有点像早期集线器。它可以把多个局域网段连起来,但如果只是无脑转发,问题并没有根本解决。所有流量仍然会被广播到所有地方,本地通信也会浪费远端网络资源,冲突概率和等待时间也会继续上升。
要连接更多计算机,不能只靠“把所有东西转发给所有人”。网络设备必须能判断数据应该留在本地,还是应该送到远方;应该走哪个方向,交给哪个下一跳。这就是路由问题。
七、为什么有了 MAC 地址还需要 IP 地址
MAC 地址可以标识网络接口,但它不适合解决全球范围内的路由问题。MAC 地址本质上更接近硬件身份,它的分配通常和厂商有关。例如某些 MAC 地址前缀属于某个厂商。这样的地址能区分设备,却不天然表达“这个设备在哪个网络里”“这个数据应该往哪个方向走”。
要跨越不同地区、不同网络传输数据,需要一种更适合分组和层级转发的地址。比如可以规定某个地区、某个组织或某个网络使用某一段地址。巴黎网络里的机器使用一类地址,马德里网络里的机器使用另一类地址。网络设备看到目标地址后,可以根据地址前缀判断数据应该留在本地,还是转发到另一个网络。
这就是 IP 地址的意义。IP 地址不是取代 MAC 地址,而是解决另一个层次的问题。MAC 地址负责局域网内的设备识别和下一跳交付,IP 地址负责更大范围的寻址和路由。一个 IP 包在跨网络传输时,目标 IP 通常保持不变,但每一跳在局域网里交付时,都需要使用当前链路上的 MAC 地址。
如果只用 MAC 地址构建全球网络,路由器就很难通过地址结构判断路径,可能需要保存海量设备的具体位置,这不具备可扩展性。IP 地址的层级结构让路由器不必知道全世界每台机器在哪里,只需要根据路由表和地址前缀决定下一跳。这个分层思想是互联网能够扩展的关键。
IPv4 就是这种思路下的协议。一个 IPv4 地址由 32 位组成,通常写成四段十进制数字,比如8.8.8.8、192.168.1.60、255.255.255.255。每段范围是 0 到 255。某些地址可以在公网中路由,某些地址则属于私有网络,只在局域网或内部网络中使用。通过 IP 地址和路由器,世界各地的网络可以连接起来,而不需要所有机器共享同一根物理总线。
八、DNS:人不应该记一堆数字地址
有了以太网和 IP,计算机之间已经可以在大范围内通信。但只靠 IP 地址使用网络并不方便。人类不擅长记大量数字地址,也不愿意每次访问一个服务都输入一串数字。访问网站时,我们更自然地输入域名,比如example.com,而不是某个具体 IP 地址。
DNS 就是为了解决这个问题。DNS 通常展开为 Domain Name System,也就是域名系统。它的核心作用是把人类可读的域名映射到机器可处理的 IP 地址。用户输入域名后,系统通过 DNS 查询得到对应 IP,然后再根据这个 IP 发起网络连接。
DNS 的存在让互联网从“机器地址系统”变成了“人也能使用的系统”。用户不需要关心某个服务当前部署在哪台机器,也不需要记住 IP 地址。服务提供方也可以调整背后的服务器地址,只要 DNS 记录更新,用户仍然使用同一个域名访问。
当然,DNS 自身也很复杂。真实世界里有递归解析、权威服务器、根服务器、缓存、TTL、负载均衡、多个记录类型等概念。但在理解ping之前,只需要抓住它最基础的作用:如果 ping 的目标是域名,系统必须先把域名解析成 IP 地址,后续网络层才能继续工作。
九、DHCP:刚接入网络时,机器怎么获得 IP
IP 地址还有一个现实问题:每台机器的地址从哪来?在服务器环境里,可以手动配置 IP、子网掩码、网关和 DNS 服务器。但对普通用户来说,手动配置网络太麻烦了。笔记本连上家里的 Wi-Fi、公司的网络或咖啡馆的热点时,不可能每次都手动选择一个没有被占用的 IP,再配置默认网关和 DNS。
DHCP 就是为了解决自动配置问题。DHCP 是 Dynamic Host Configuration Protocol,动态主机配置协议。机器接入网络后,可以通过 DHCP 从网络中的路由器或 DHCP 服务器那里获得 IP 地址,同时拿到默认网关、DNS 服务器等配置信息。这样,普通用户只需要连上网络,系统就能自动完成基础配置。
这里有一个很有意思的细节:一台刚接入网络的机器还没有 IP 地址,也不知道 DHCP 服务器是谁,那它怎么发请求?答案是广播。机器可以向本地网络中的所有设备发出广播请求,大意是“我需要网络配置,谁能给我分配地址?”在 IPv4 中,255.255.255.255可以作为受限广播地址,用来向本地网络所有主机发送消息。DHCP 服务器收到请求后,再返回可用地址和相关配置。
这说明网络协议里有些地址并不表示普通主机,而是具有特殊语义。广播地址用于本地网络广播,私有地址用于内部网络,环回地址用于本机通信。理解这些特殊地址,有助于理解网络不是简单的“一个数字对应一台机器”,而是一套带有层次和规则的地址体系。
十、协议会越来越多,但学习不能一开始就被淹没
到这里,已经出现了很多概念:物理信号、比特编码、网卡、总线、以太网、MAC 地址、数据片段、冲突检测、校验和、IP 地址、路由器、DNS、DHCP、广播地址。真实网络世界里还远不止这些。还会有用于动态交换路由信息的协议,用于传输网页的 HTTP,用于可靠传输的 TCP,用于低延迟但允许丢包的 UDP,用于安全通信的 TLS,用于文件传输、邮件、远程登录、时间同步和各种业务系统的协议。
如果一开始就把所有协议一次性铺开,学习网络会变得非常痛苦。更好的方式是从一个具体任务出发,只在需要的时候引入下一层知识。ping正好适合作为这样的入口。它简单到几乎每个使用计算机的人都见过,但又足够底层,能把 IP、ICMP、路由、局域网交付、操作系统网络栈等概念串起来。
ping关心的问题也足够基础:给定一个目标 IP,当前机器能不能到达它?如果能到达,往返需要多久?如果不能到达,是本地网络不可达、目标机器不存在,还是中间路径被阻断?虽然ping的输出很简单,但它观察的是整个网络路径上的连通性。
在 Windows 上,系统自带ping.exe。给它一个 IP 地址,它就会尝试向目标发送请求。例如,8.8.8.8是一个常见的公网地址,请求会经过互联网中的多个跳点。再比如,192.168.1.60这样的地址通常属于本地私有网络,如果这个地址当前没有分配给任何设备,本地路由器可能就能判断它不可达,并返回相应结果。
可以用类似下面的命令测试:
ping 8.8.8.8也可以测试局域网中的某个地址:
ping 192.168.1.60从使用者角度看,这只是一个命令。从系统角度看,背后要经过多层处理。操作系统要构造网络数据包,判断目标地址是否属于本地网络,必要时把数据交给默认网关;如果目标在局域网内,还要找到目标的 MAC 地址;网卡要把数据发送出去;中间设备要按照路由规则转发;目标机器收到请求后要生成响应;响应再沿着网络路径返回本机,最终由ping程序统计耗时和结果。
十一、为什么要自己实现 ping
直接使用现成的ping工具当然很方便,但它隐藏了太多细节。屏幕上看到的只是结果,而不是过程。要真正理解网络,就不能只满足于“这个命令能用”,还要知道它为什么能用,以及失败时可能失败在哪一层。
自己实现一个ping,价值就在于把这些抽象逐层拆开。首先要理解ping使用的不是 TCP,也不是普通应用层请求,而是 ICMP。然后要理解 ICMP 运行在 IP 之上,所以必须知道 IP 包如何构造、如何发送。继续往下,还要理解操作系统如何允许用户程序发送这种包,为什么某些系统上运行 ping 需要管理员权限,为什么有些网络环境会禁止 ICMP,为什么目标机器不响应 ping 不一定代表目标服务不可用。
再往下,还会涉及本地网络交付。即使目标是一个公网 IP,本机通常也不是直接把数据发给目标机器,而是先发给默认网关。为了把数据交给网关,本机需要知道网关在局域网中的 MAC 地址。这就会牵出 ARP 等协议。虽然第一篇还没有进入这些实现细节,但它已经把问题链条铺好了:ping不是孤立工具,而是网络协议栈上的一个观察窗口。
学习系统知识时,最怕只记名词。MAC、IP、DNS、DHCP、ICMP、路由器、广播地址这些词如果孤立看,很容易变成背诵材料。但如果从“我想知道一台机器能不能到达”这个任务出发,它们之间的关系就清楚了。要 ping 一个域名,先需要 DNS;要向目标 IP 发包,需要 IP;要跨网络发送,需要路由器;要在局域网交付给下一跳,需要 MAC;要让机器获得自己的网络配置,可能需要 DHCP;要在底层发送数据,还需要网卡和物理介质。
十二、用一条主线理解网络分层
可以把前面的内容串成一条主线。最底层的问题是:怎样把 0 和 1 从一台机器送到另一台机器?这需要电缆、无线信号、光纤等物理介质,也需要把比特编码成可传输信号的办法。这个层次关心的是物理世界中的信号传输。
再上一层的问题是:如果多台机器共享同一片通信介质,怎样知道数据来自谁、发给谁,怎样避免大家同时发送,怎样发现数据损坏?这就需要 MAC 地址、数据帧、冲突处理和校验机制。以太网解决的就是这种局域网内部通信问题。
继续往上,问题变成:局域网不能无限扩大,怎样把不同网络连接起来?这就需要 IP 地址和路由。IP 地址提供层级化寻址,路由器根据地址前缀和路由表决定数据应该走向哪里。MAC 地址解决局部交付,IP 地址解决跨网络传输,两者并不是重复关系。
再往上,问题变成人的使用体验:人不想记数字 IP 地址,所以需要 DNS 把域名转换为 IP;机器刚加入网络时不想手动配置地址,所以需要 DHCP 自动分配 IP、网关和 DNS。再往上,才是各种应用协议和工具,比如浏览器使用的 HTTP、远程登录使用的 SSH、测试连通性的 ping。
这条主线能帮助我们避免把网络学成一堆孤立概念。每一层都在解决上一层暴露出来的新问题,每一层也都依赖下一层提供的能力。理解ping的过程,其实就是从应用工具一路向下看,直到接近软件和硬件的边界。
十三、这一篇到底讲清了什么
这一篇并不是严格意义上的网络历史课,而是用一种构造式的方式解释计算机网络为什么会变成今天这样。早期计算机昂贵,所以需要共享;终端可以远程访问大型机,但那还不是真正的计算机互联;多台计算机之间要交换数据,就需要网络;两台机器可以直接连线,多台机器直接全连接会失控;共享总线减少了线缆,却需要地址、分片、冲突处理和错误检测;局域网不能扩展到全世界,所以需要路由和 IP;人类不愿意记 IP,所以需要 DNS;机器不愿意手动配置 IP,所以需要 DHCP;想知道一个目标是否可达,就可以从 ping 开始。
这套讲法的重点不在于背协议名称,而在于理解协议背后的问题。每个协议都是在解决一个具体困难:MAC 地址解决共享介质中的身份识别,IP 地址解决跨网络路由,DNS 解决名字和地址的映射,DHCP 解决自动配置,ICMP 和 ping 则提供一种观察网络连通性的方式。
真正开始写自己的ping时,就不会只是机械地构造一个 ICMP Echo Request,而是能知道它要被包在 IP 里面,IP 又要通过本地网络交付给下一跳,下一跳再根据路由表继续转发。最终屏幕上看到的延迟数字,其实是数据从本机出发、穿过一层层协议和设备、到达目标再返回的结果。
ping之所以适合作为网络学习入口,就是因为它足够简单,也足够深。简单在于用户只需要输入一个地址,深在于它背后连接着整个协议栈。自己实现它,就是沿着这条路径把抽象层一层层剥开,直到接近软件世界和物理世界的边界。
网络看起来复杂,但它并不是一开始就复杂。复杂性来自规模、共享、距离、错误、名字、配置和路由等现实问题。每解决一个问题,就多一层抽象;每多一层抽象,系统就更强大,也更难一眼看透。理解这些抽象如何产生,比单独记住某个协议字段更重要。
从这个角度看,自己实现ping不只是写一个命令行工具,而是在用一个小任务重新走一遍互联网的形成逻辑。先理解为什么要连接计算机,再理解怎样连接两台计算机,接着理解怎样连接很多计算机,最后再理解为什么需要 IP、DNS、DHCP 和 ICMP。等这些关系理顺之后,后面真正进入代码实现时,每一个系统调用、每一个包头字段、每一次网络响应都会更容易理解。