很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。直到有一天,你希望与另一台电脑 B 建立通信,于是你们各开了一个网口,用一根网线连接了起来。用一根网线连接起来怎么就能"通信"了呢?我可以给你讲 IO、讲中断、讲缓冲区,但这不是研究网络时该关心的问题。
如果你纠结,要么去研究一下操作系统是如何处理网络 IO 的,要么去研究一下包是如何被网卡转换成电信号发送出去的,要么就仅仅把它当做电脑里有个小人在开枪吧~反正,你们就是连起来了,并且可以通信。
有一天,一个新伙伴 C 加入了,但聪明的你们很快发现,可以每个人开两个网口,用一共三根网线,彼此相连。随着越来越多的人加入,你发现身上开的网口实在太多了,而且网线密密麻麻,混乱不堪。
于是你们发明了一个中间设备,你们将网线都插到这个设备上,由这个设备做转发,就可以彼此之间通信了,本质上和原来一样,只不过网口的数量和网线的数量减少了,不再那么混乱。你给它取名叫集线器,它仅仅是无脑将电信号转发到所有出口(广播),不做任何处理,你觉得它是没有智商的,因此把人家定性在了物理层。
如果把这个集线器弄得更智能一些,只发给目标 MAC 地址指向的那台电脑,就好了。
虽然只比集线器多了这一点点区别,但看起来似乎有智能了,你把这东西叫做交换机。也正因为这一点点智能,你把它放在了另一个层级,数据链路层。交换机内部维护一张 MAC 地址表,记录着每一个 MAC 地址的设备,连接在其哪一个端口上。假如你仍然要发给 B 一个数据包,构造了如下的数据结构从网口出去。
到达交换机时,交换机内部通过自己维护的 MAC 地址表,发现目标机器 B 的 MAC 地址 bb-bb-bb-bb-bb-bb 映射到了端口 1 上,于是把数据从 1 号端口发给了 B,完事。你给这个通过这样传输方式而组成的小范围的网络,叫做以太网。
交换机已经无法记录如此庞大的映射关系了。
此时你动了歪脑筋,你发现了问题的根本在于,连出去的那根红色的网线,后面不知道有多少个设备不断地连接进来,从而使得地址表越来越大。那我可不可以让那根红色的网线,接入一个新的设备,这个设备就跟电脑一样有自己独立的 MAC 地址,而且同时还能帮我把数据包做一次转发呢?这个设备就是路由器,它的功能就是,作为一台独立的拥有 MAC 地址的设备,并且可以帮我把数据包做一次转发,你把它定在了网络层。
注意,路由器的每一个端口,都有独立的 MAC 地址。
好了,总结一下,到目前为止就几条规则。从各个节点的视角来看:电脑视角:首先我要知道我的 IP 以及对方的 IP。通过子网掩码判断我们是否在同一个子网。在同一个子网就通过 arp 获取对方 mac 地址直接扔出去。不在同一个子网就通过 arp 获取默认网关的 mac 地址直接扔出去。交换机视角:我收到的数据包必须有目标 MAC 地址。
通过 MAC 地址表查映射关系。查到了就按照映射关系从我的指定端口发出去。查不到就所有端口都发出去。路由器视角:我收到的数据包必须有目标 IP 地址。通过路由表查映射关系。查到了就按照映射关系从我的指定端口发出去(不在任何一个子网范围,走其路由器 的默认网关也是查到了)。查不到则返回一个路由不可达的数据包。
网络层(IP 协议)本身没有传输包的功能,包的实际传输是委托给数据链路层(以太网中的交换机)来实现的。
交换机中有 MAC 地址表用于映射 MAC 地址和它的端口。路由器中有路由表用于映射 IP 地址(段)和它的端口。电脑和路由器中都有 arp 缓存表用于缓存 IP 和 MAC 地址的映射关系。
这三张表是怎么来的:MAC 地址表是通过以太网内各节点之间不断通过交换机通信,不断完善起来的。路由表是各种路由算法 + 人工配置逐步完善起来的。arp 缓存表是不断通过 arp 协议的请求逐步完善起来的。