计算机网络(一)互联网与 IP
由于最近研究番剧、写番剧评论的时候用到了的 BT 下载,找了个开源的 BT 下载器,然后配置这个下载器时遇到了许多困难,其中就有好多计算机网络的术语。由于向来对这种只按照教程去做,但是不知道原理的事情很反感。而计算机网络又是下学期我们要学的东西,于是打算先学习一下。
本文是在学习斯坦福大学的 CS144 课程时的学习笔记,由于 b 站只有机翻字幕,所以可能有一些术语不知道是否会使用准确。如有错误,还请劳烦指正。
本章目录
1.1 应用程序生命中的一天
网络应用程序的基本模型为,有两台计算机,它们可以通过网络进行通信。这个模型最常见的实现模式为双向、可靠的字节流,即在计算机 A 上运行的应用程序可以向网络传输数据,并且数据可以被计算机 B 上运行的程序接收和读取,同理,计算机 A 也可以以此种方式接收和读取计算机 B 的数据。
所以字节流模型就是计算机 B 等待外部计算机的连接,而计算机 A 与 B 建立连接后,它们之间就可以进行上述的数据传输,且任意一方都可以关闭连接。所以在这种模型下,服务器也可以关闭连接,比如当我们看到 "Connection Reset" 这样的错误时,意味着服务器在浏览器未预料的情况下中断了连接,而 "Massage Refused" 或者 "Timed Out" 的情况则是服务器拒绝了连接或者服务器长时间未响应的情况。我们之后会提到这个模型的原理的细节。
至此,我们已经了解了网络应用程序的基本通信方式,我们来看看一些实例:
例 1.1.1 万维网(用户-服务器体系)
万维网使用 HTTP(超文本传输协议)进行工作。在 HTTP 中,用户端打开与服务器的连接并向其发送指令。最常见的指令是用于请求页面的 GET。HTTP被设计为以文档为中心的程序通信方式。
例如,当我们要访问一个 HTTP 的网页时,在浏览器中输入网址后,浏览器会尝试与该网址对应的服务器建立连接并向其发送 GET 请求。然后服务器收到请求,并发送响应。由于 HTTP 以文档为中心,所以 HTTP 的请求全部都是一些字符串。基本模型即:用户端通过写入连接来发送请求;服务器读取,处理请求并写入对连接的响应;然后再用户端读取。
例 1.1.2 BitTorrent(P2P 体系)
BitTorrent(简称 BT)是一个程序,允许人们共享和交换大文件。不同于万维网,用户端在 BT 中请求来自其他用户的文件。为了使单个用户端可以并行地从许多其他用户端发出请求,BT 破坏了文件并且分成称为片段的数据块。当一个用户从另一个用户下载完整的作品时,它会告诉其他用户它具有该部分,因此他们也可以下载它。这些合作用户的集合称为群组。因此,我们下面讨论用户如何加入或离开群组。
BT 使用与万维网完全相同的机制:可靠的双向数据流。但是它以稍微复杂的方式使用它。当用户想要下载文件时,它首先必须找到一个种子文件。这个种子文件描述了有关您要下载的数据文件的一些信息。它还会告诉 BT 该数据的 Tracker。Tracker 是一个结点,用于跟踪哪些用户是群组的成员。如果我们要加入一个群组,我们的 BT 用户端会通过 HTTP 与 Tracker 建立连接,以请求当前在该群组中的用户列表。 然后我们的 BT 用户端打开与其中一些用户端的连接,并开始请求文件碎片。这些用户反过来可以从我们这里取走文件。此外,当新用户端加入群时,Tracker 可能会告诉该新用户端连接到您的用户端。 因此,我们需要一个密集的连接网,而不是一个用户端和一个服务器的连接图。
例 1.1.3 Skype(混合型)
在看了上面的例子后,下面我们来分析一个较为复杂的例子:社交软件 Skype 中的语言通话服务和视频通话服务的工作原理。这个软件实现通话服务有三种情况:
- 情况 1
两个用户都可以直接连接到公网,它们互相之间可以直接建立连接,然后进行通信。但是这里直接是两个用户之间的通信,而不是用户和服务器之间的通信。
- 情况 2
但是现在并不是所有的计算机都是直接连接的公网(Internet),比如一个企业经常就会有专用路由器来建构企业的专用网,生活中人们也会在家中装小型家用无线路由器,这些路由器都被称为 NAT(网络地址转换)路由器。所有与 NAT 路由器相连的用户都需要通过 NAT 来与公网建立连接,这也导致这些用户无法接收到来自公网的连接请求,而只能向公网中的用户或服务器发送连接请求。
在下面的情况中,如果用户 A 还像情况 1 那样,想要直接通过发送请求的方式与用户 B 建立连接,那么由于用户 B 是通过 NAT 路由器访问的公网,就会导致用户 B 收不到来自用户 A 的请求,进而使得连接失败。
为了解决这个问题,Skype 引入了集合服务器(Rendezvous)。集合服务器直接与公网相连。当用户登录 Skype 时,Skype 客户端就会向集合服务器发送连接请求并与之建立连接。这时当用户 A 想要与用户 B 进行视频通话时,用户 A 的通话请求会被当作一条信息送到集合服务器,由于用户 B 已经与集合服务器建立连接,所以集合服务器可以直接将这个信息转达给用户 B,这时用户 B 的电脑界面上就会显示“用户 A 正在请求视频通话”的通知。如果用户 B 选择接听,那么用户 B 会直接发送一条连接请求给用户 A,最终两个用户建立连接,实现正常通信。我们管这样本来是用户 A 希望连接用户 B,而最终却是用户 B 向用户 A 发送连接请求的模式叫做反向连接(Reverse Connection)。
- 情况 3
当然还有一种情况是两个用户都是通过 NAT 路由器访问的公网。这导致两个用户彼此都不能收到来自对方的连接请求,无法建立连接。
这时 Skype 又引入了中继服务器(Relay)。同样,中继服务器也直接与公网相连。当用户登录 Skype 时,Skype 客户端同样会向中继服务器发出连接请求并建立连接。当用户 A 希望和用户 B 进行通话时,只需要向中继服务器发出请求,中继服务器将这个信息传递给用户 B,如果用户 B 接受,那么用户 A 和用户 B 就会通过中继服务器相互发送数据,即它们通过中继服务器实现了间接的通信。
1.2 四层互联网模式
我们已经了解了不同的应用程序在使用双向、可靠的字节流进行通信的方式,然而这些方式的实现需要网络不同部分的的共同努力。下面我们就来了解早期互联网先驱创造的“4 层网络模型”,这是描述互联网的层级结构的模型。有了它,被新开发的应用程序就可以直接与已有的网络各个层之间进行对接,提高网络的复用性。
下面就是这 4 层结构,我们会从最底层开始介绍,我们会看到每一层都是在下面一层的基础上构建的服务,最终形成一个可靠的双向字节流通信系统。
1.2.1 链路层(Link)
互联网由终端主机,链接和路由器组成。而其中数据以数据包(Data Package)的形式,通过每个链接逐结点传递。数据包包含我们要传递的数据以及报头,标头内容包含数据包的来源地和目的地等等信息。而链路层的工作就是一次通过一个链路传送数据。
1.2.2 网络层(Network)
网络层的工作是通过 Internet 以端到端传输的模式将数据包传输到目的地。在网络层数据包被称为数据报(Datagrams)。在传输的时候,网络层将数据报交给链路层,让它将数据报传到指定地点,即链路层为网络层提供了传输数据报的服务。
具体地,在传输过程中,数据来源处的链路层会按照网络层的指示,将数据报传输到一个路由器(Router),这个路由器的链路层收到数据报后会先将其发给该路由器的网络层,然后由网络层根据数据报报头中的传输目的地确定接下来应该将数据包传输到哪个链接,再将数据包给该路由器的链路层让其进行传输,经历多个路由器的传输后,数据报会到达直接与目的地相连的路由器,最终到达目的地的网络层。
注意在传输过程中,网络层并不需要关系链路层是如何实现的传输,只需要通过层与层之间提供的 API 进行交互。实际上,每相邻两层之间的交流都是这样,且这使得每一层都可以被模块化,进而具有很好的通用性。
在 Internet 中,如果要在网络层中传输数据,必须使用 IP(Internet Protocol,互联网协议)。这里我们介绍 IP 的两个特点:不保证数据一定能被传输到目的地;不保证数据传输过程中不会损坏,顺序不会被打乱。
显然 IP 以上的两个特点会导致互联网传输数据的没有可靠性可言,所以我们需要在 IP 之上再运行另一个协议,而这就是传输层的工作。
1.2.3 传输层(Transport)
最常见的传输层协议就是 TCP(Transmission Control Protocol,传输控制协议),TCP 会确保 Internet 中的数据可以以正确的顺序传递到目的地。注意到传输过程中不会访问路由器的传输层,所以 TCP 要做的就是在数据缺失或损坏的情况下,请求数据来源的那一端重新发送数据;而在数据顺序被打乱的情况下,通过排序让数据重新有序。
TCP 的可靠性使得其在 Web 客户端和社交软件客户端等应用程序中得到广泛的应用。
而用于视频会议的程序如果以数据包的形式发送一段视频,如果传输过程中造成了数据损坏,由于是实时通话,此时可能来不及等待数据再次被重新发送。而且即使一些信息丢失,对于用户的影响也不会太大(可能只影响通话的流畅度等)。这时我们就需要一个比 TCP 更为简单的协议:UDP(User Datagram Protocol,用户数据报协议)。UDP 只是捆绑应用程序数据,然后将其交给网络层传输。
除了 TCP 和 UDP 之外,还有许多其他的传输层协议,但最常见的还是它们。
1.2.4 应用层(Application)
和前面已经介绍过的一样,我们已经看到了不同的应用程序在使用双向、可靠的字节流进行通信的方式。不同应用程序可能在这一层采取不同的通讯协议,并通过其下方的传输层收发数据。(而传输层整理好数据会将数据给网络层,然后网络层再给链路层,然后就是传输,到达目的地再将数据逐层向上传递)
我们发现互联网的信息传输如果不关注传输过程,似乎就像数据源的每一层只和目的地的同层进行通话。
我们发现,在网络层只有 IP 一个协议,而在其他层都有不同的协议或者实现方式,所以 IP 被称为互联网的“瘦腰”。
在 20 世纪 80 年代,国际标准组织(ISO)创建了一个 7 层模型来表示任何类型的网络,它被称为 7 层开放系统互连或 OSI 模型。但是它已经被四层互联网模式所取代,下图是它们的对应关系:
不过即使被取代,但是 7 层模型对每层的编号仍然保留了下来,比如现在网络工程师常常把链路层叫做第 2 层,网络层叫做第 3 层,应用层叫做第 7 层。
1.3 IP 服务模型
我们现在来专注于网络层提供的服务,即了解 IP 的服务模型。
IP 规定数据格式必须为 IP 数据报(一种 IP 规定的数据包)的格式,IP 数据报(Datagram)有报头(Header)和数据(Data)组成。
当传输层要发送数据时,它将数据段传给网络层。网络层将数据放入 IP 数据报中,进而传给链路层。链路层将数据封装到帧中,然后开始把数据向路由器传输。
IP 服务有如下四个特征:
- 通过数据报传输数据,且每个数据报之间的路线相互独立,且是逐跳(Hop-by-hop)传输,即传输过程中需要知道数据最终目的地的同时,还需要知道数据接下来要传到哪个路由器;
IP 数据报的报头中包含 IP 源地址(IP SA, IP Source Address)和 IP 目的地址(IP DA, IP Destination Address)。在实现逐条传输时,每个路由器都会有存有一个转发表,转发表会告诉路由器,数据到达其目的地的路径上的下一个路由器是什么。而 IP SA 则会告诉目的地一端是谁发送的这条数据。
- 不可靠性,数据包中途可能会被丢弃;
但是仅在必要时丢弃数据包,比如遇到了网络堵塞等情况。除此之外,数据包可能还会被送到错误的地址。而且数据包被丢弃,IP 也不会告诉发送方。出现其他错误,IP 也不保证会检测到错误。
- 尽可能的高效;
- 无连接(Connectionless)传输,即数据报被发出后,IP 协议不会维护其后续状态信息。
我们从 IP 的特点中看到了其设计的非常简单,为什么它设计的那么简单?我们有如下理由:
- 维护成本低;
- 更好的实现端到端原则,即将所有技术细节尽可能在数据收发终端实现,而对传输网络本身要求少;
- 可以满足传输层多样的协议要求;
- 对链路层的要求少,便于与链路层的不同实现都可以很好对接。
IP 除此之外还有一些细节。比如 IP 会尝试阻止一个数据报被几个路由器传来传去,形成循环。IP 通过在数据报报头中记录一个 TTL(Time To Live)字段。在数据报发出时,它可能以 128 开始,每经过一个路由器该数就减一,直到其减为 0,就直接丢弃该数据报。
如果数据太长,IP 也会自动为其提供分段。IP 的报头中还有校验和字段,可以随时检测数据传输过程中是否传输出错。
当前有两种版本的 IP: IPv4(32 位地址), IPv6(128 位地址),由于 IPv4 地址已经用完,所以目前在向 IPv6 过渡。
IP 也有一定的拓展性。
我们下面来看一下 IPv4 数据报的报头(灰色部分)。
解释一下前面没有提到过的东西:
第二行是用于给 IP 拆分的长数据段编号的。
Protocol ID 是表明这个数据报中的数据使用了哪种链路层协议。
1.4 数据包生命中的一天
我们前面已经知道数据包传输的大体流程,首先传输层会将要传输的数据给网络层,网络层将长数据分段封装在 IP 数据包里,然后再向目的地传输。
我们来看看实际中它们是怎么工作的。首先看传输层。我们这里以 TCP 为例。在客户端和服务器之间的通信,服务器被动的接受连接请求,客户端先向服务其发送连接请求,然后服务器回应请求。这个流程被称为 “TCP 三次握手(Three Way Handshake)”。
具体流程为:
- 客户端向服务器发送“同步序列编号(Synchronize Sequence Numbers)”信号(SYN)。
- 服务器做出响应,向客户端回应“同步序列编号和确认(Acknowledge)”信号(SYN-ACK)。
- 客户端响应,向服务器发送确认信号(ACK)。
所以三次握手可以被描述为 “SYN, SYN-ACK, ACK”
注意网络层和传输层工作的不同之处,网络层只负责将数据传输到某个特定的计算机,而传输层要负责将数据传输到计算机上某个特定的应用程序。为了区分一个计算机上运行的多个应用程序,计算机会给应用程序分配端口(Port)。所以为了传输一个 TCP 数据流,我们需要两个地址,一个是计算机在网络层的 IP 地址,还有一个是我们目的应用程序在该计算机上所使用的 TCP 端口。
我们在来看数据包是如何传输的,数据包是通过路由器逐跳传输到服务器的。而一个路由器又有许多其他不同的链接,所以当路由器收到数据,它需要决定这个数据的下一跳要去往哪个链接。路由器是通过转发表(Forwarding Table)来完成这个工作的。
转发表由多个 IP 模式地址(即将转发表中的 IP 地址字符串匹配中的模式串)和与之对应的下一跳的链接编号组成。当数据包到达时,路由器会将数据包目的地址与其转发表中的 IP 地址进行匹配,然后将数据包发送给,与数据包目的地址匹配了最长的前缀的 IP 模式地址所对应的链接。如果没有与之匹配的 IP 模式地址,那么就会选择默认的链接传输该数据包。
当然,每个路由器都有自己的 IP 地址,所以我们也可以通过网络访问路由器上的程序(比如登录家用路由器账号进行路由器的配置)。
1.5 分组交换原理
在 Internet 设计原则中,分组交换原则是最有争议且具有革命性特点的一种原理。现在我们来简单介绍分组交换原理。
分组交换原理即将要传输的数据拆成一个个小段装入数据包中,然后每个数据包都装有目的地信息,使得它们可以通过路由器和链接被传输到正确的目的地。同时还要求每个数据包是彼此独立的,即对于同一组数据所拆成的多个数据包,路由器会分别决定它们的传输路线,同等的看待它们。
分组交换原理一个简单的实现方式是,对于一个数据包,我们在报头就填好该数据包的从本地出发的到达目的地的整体行进路线。但是这样会使得整个网络的各个结点暴露无遗,有失安全性。所以现在通常采用的是数据包报头只包含目的地信息,有途径的各个路由器确定数据包具体的传输路线。
分组交换有两个好处:
- 不需要关心数据包的具体行进路线。
- 便于链接共享。
链接共享举一个例子,即如果有两个设备同时连着一个 Wi-Fi。一个在下载文件,另一个在浏览网页。由于浏览网页只有加载新页面时需要收发新的数据包,所以在浏览网页时,Wi-Fi 可以全速下载文件。而在需要加载新网页的时候,Wi-Fi 同时给下载文件和加载网页分配传输速度。
1.6 封装原理
正如我们之前所见的,对于应用层要传输的数据,会被传输层根据相应协议封装成数据段,而传输层的数据段会被网络层封装成 IP 数据包,最后 IP 数据包被链路层封装成帧,并向目的地传输。
而封装的意思就是在源数据头尾各添加自己层所应用协议的头部和尾部。
有了封装技术,我们其实可以通过封装递归的对协议进行分层。我们可以以虚拟专用网(Virtual Private Network,VPN)为例。我们可以通过 VPN 实现特定的端到端加密通信。一个企业通常有专用的内网,但是我们可以通过一些加密手段,比如 TLS(Transport Layer Security)来使得我们数据包的内容收到保护。
即我们数据包从内到外包含了:内部 HTTP 协议数据,内部 TCP 协议数据段,内部 IP 数据包,TLS 加密内部的数据,外部 TCP 协议数据段,外部 IP 数据包,外部帧。
其中内部的数据包含了我们真正想发送的数据和我们真正要访问的目的地。而外部的 TCP 与 IP 协议则是包含了我们所使用 VPN 网关的地址。这样我们要传什么数据,传数据给谁,外界是无法通过正常手段看出的,于是就保证了安全性。
1.7 字节顺序
在计算机中数据是根据字节存储的,而一个字节是 8 位。但是我们知道现在使用的计算机大多都已经变成 64 位了,即计算机内存中的地址是用一个 64 位二进制数表示的。所以计算机的中会存有许多多字节变量,这时候就出现了两种字节顺序。即存数时如果将数的低位放在低字节位(地址较低的存储单元),将数的高位放在地址放在高字节位,这样的字节顺序我们称为小字节序(Little Median),反之,称为大字节序(Big Median)。
比如上图即为 1024 的 16 进制 0x0400 如果以不同字节形式存在存储单元中的情况。
由于不同的处理器有不同的字节顺序,所以会导致由于字节顺序不一致,网络数据接收端无法正确识别数据。不过一方面我们可以编写程序来判断我们设备是什么字节顺序,另一方面,C 语言的 arpa/inet.h
库提供了 htons()
, ntohs()
, htonl()
, ntohl()
四个函数来帮助我们实现不同字节顺序之间的转换。(htons 即 "host to network short", ntohl 即 "network to host long",其余同理)
但是在写代码时仍然要小心慎重。譬如有时我们将数据转化为小字节序数据,而处理器本身是按照大字节序处理的数据,就会导致转化后的数据在和本地立即数作比较时出错。
1.8 IPv4 地址
我们前面已经谈论过 IP 地址在网络中的用法了,现在我们来看看 IPv4 地址的格式。
由于要建立统一的网络,所以就需要有统一格式的地址,且每一个计算机的 IPv4 地址应该是唯一的。但是现在却有了一些特殊情况,我们之后会讨论,现在我们假设一台计算机的 IPv4 地址是唯一的。
IPv4 地址为 32 位二进制数。为了方便表示和识别,我们将这个二进制数每 8 位会用 . 隔开,即形如 a.b.c.d 的格式,并且 a, b, c, d 均为不超过 255 的十进制数。譬如 171.64.64.64 就是一个 IP 地址。我们将这种二进制数的表示方法叫做点分十进制记法。
为了通过地址分辨两台设备是否在一个网络里(如果两台机器在同一个网络里我们可以理解为它们之间直接有线路相连,不需要再借助路由器进行通信),我们给出了子网掩码(Netmask)的概念:即每一台计算机除了 IPv4 地址外,还有一个由 32 位二进制数组成的子网掩码。为了方便,我们要求子网掩码的二进制表示必须是一串从最高位开始的连续的 1。子网掩码也使用点分十进制记法,我们将子网掩码中 1 的个数子网掩码的位数。譬如 255.255.255.0 是一个 24 位的子网掩码。显然若使用点分十进制记法,子网掩码中只会出现 0、128、192、224、240、248,252、254、255 这 9 个数字。
我们可以通过子网掩码判断计算机 A 和计算机 B 在不在一个网络里:如果计算机 B 的 IPv4 地址与计算机 A 的子网掩码按位与得到的结果和计算机 A 的 IPv4 地址与计算机 A 的子网掩码按位与得到的结果相同,那么它们就在一个网络里。
而子网掩码的某位为 1,即表明在同一个网络的 IPv4 地址需要和当前设备的地址在这位相同。故一台设备的子网掩码如果为 \(k\) 位,那么只要与其 IPv4 地址的前 \(k\) 位匹配的另一台设备就与其在同一网络中。由此也可以看出,子网掩码位数越少,当前设备所在的网络就越大。
而 IPv4 地址的分配方式之前是将 32 地址中的一个前缀做为网络地址,其余部分做为设备的标号。即一个设备会被先分配到一个个小的子网里,然后再被分配一个编号。比如下面三类:
由于这样分配地址缺乏灵活性,故现在较为常见的则以无类域间路由选择(Classless Inter-Domain Routing,CIDR)的形式分配地址,也就是说不对网络地址的长度做限制。这时候一个网络地址,就可以生成 \(2^k\) 个 IPv4 地址。而且这些地址的子网掩码均相同,且这个子网掩码为 \(32 - k\) 位。对于这种分配地址的方式,我们会使用譬如 171.64.0.0/16 的形式来表示分配情况,这里是指网络地址长度为 16,置为 17164,而后面 8 为就是编号,所以这里表示的是在 171.64.0.0 ~ 171.64.255.255 这个区间的 IPv4 地址。每一次分配地址,我们都像这样分配出一块地址出去。
1.9 最长前缀匹配
一个路由器由于有许多链接,所以对于一个数据包来说,路由器需要决定它的下一跳是什么。
正如前面所提,路由器是根据转发表来决定其下一跳的。而路由器的转发表就是形如下图的样子:
其中 x
是通配符。对于一个数据包的目的地址,路由器会依次与这些地址进行二进制逐位匹配,最终选择匹配到的最长的模式地址所对应的链接进行数据传输。当然,我们通常会使用 CIDR 的形式来实现通配符表示。
其中 0.0.0.0/0 其实也就是实现了当没有地址与数据包目的地址匹配时,数据包会被传输至默认链接。
比如 171.33.1.1 就与 171.33.0.0/16 匹配,由于是唯一匹配所以默认就是最长的匹配,此时该数据包会被发送到链接 5。
1.10 地址解析协议(ARP)
在传输数据包时,我们已经知道了目的地的 IP 地址,但是在物理层面上的传输的工作仍然需要链路层来完成,所以我们需要根据目的地的 IP 地址来得出目的地的链路层地址。
我们需要先了解一些链路层的工作细节。在链路层,所有网络帧的收发都是通过网卡实现的。和每台设备都有 IP 地址一样,每个网卡也都有一个 48 位的链路层地址,它会确定网卡在以太网中的位置。所以在网络层会叫做“此主机”,在链路层会叫做“此以太网卡”。
下图中 00:13:72:4c:d9:6a 就是一个链路层地址,每 8 位以冒号间隔且用 16 进制表示。
我们在前面说一台机器只有一个对应的 IP,但是在上图我们会发现在网关(路由器)处会有问题。一台路由器通常要处理一个区域的主机的数据包收发服务,这些主机的地址通常不是连续的。而问题在于,根据定义,路由器与其相连的主机在同一个子网中,所以路由器的子网掩码必须符合此要求。但是这就会导致上图中路由器的子网掩码过小(准确的说,只有 1 位),使得如果本来路由器只是在一个较小的子网中,但是其子网掩码却表明其在一个非常大的子网中,这显然是不合理的。所以通常路由器的每个链接接口都会被分配一个 IP 地址和一个子网掩码。
下面我们来看,如果结点 A 想给结点 B 发数据包,这时结点 A 会根据自己的 IP 地址和子网掩码还有目的地的 IP 地址判断其与目的地是否在一个子网里。根据下图中的子网掩码可以计算得出结点 A 和结点 B 不在一个子网里,它们之间通信需要网关。这种情况下,A 会发送一个目的地的 IP 地址为 171.43.22.5(B 的 IP 地址)的网络层数据包给链路层,但是由于需要通过网关,所以链路层将其封装成帧的时候需要将目的地的链路层地址设置为 0:18:e7:f3:ce:1a(网关对应接口的链路层地址)。
网络帧通过链路层到达网关后被网关解包,得到网络帧里面 IP 数据包的目的地地址信息。网关通过查转发表来决定数据包下一跳的去向。确定选择去向结点 B 的链接后,再次将此 IP 数据包封装成网络帧,并将该网络帧的目的地链路层地址设置为 9:9:9:9:9:9(B 的链路层地址),最终数据包被传输到 B。
了解了这些以后,我们再来看地址解析协议(Address Resolution Protocol, ARP)的工作:即当传送数据包时,我们只知道目的地的 IP 地址,但是不知道目的地的链路层地址,ARP 就可以帮助我们找到对应的链路层地址。其工作方式即为当上述找不到链路层地址的情况发生时,我们会在我们的子网里(或者说广播域)发送一个 ARP 广播(数据包),其中会包含我们的 IP 地址与链路层地址和我们需要建立连接的目的地的 IP 地址。在广播域内的所有机器都会收到这个数据包,并将这个数据包中的目的地 IP 地址与自己的地址比对,如果不符则直接丢弃,如果相符则根据 ARP 广播中我们留下的地址向我们发送一条回应信息,在信息中给出其链路层地址。这样我们就可以获得了目的地的链路层地址,并且会记在我们本地的 IP 地址和链路层地址对应表中。
上图就是一种 ARP 数据包格式,其中 Hardware 记录的是这个 ARP 数据包请求或响应哪个链路层,而 Protocol 记录的是这个数据包请求或响应所应用的网络协议,长度字段指定链路层和网络层地址的字节数,Opcode 则是表明该数据包是请求还是响应。