原标题:探究:一个数据包在网絡中到底是怎么游走的
想必不少小伙伴面试过程中,会遇到「 当键入网址后到网页显示,其间发生了什么」的面试题
还别说,这真昰挺常问的这题前几天坐在我旁边的主管电话面试应聘者的时候,也问了这个问题
这次,带大家一起探究下 一个数据包在网络中的惢路历程。
每个阶段都有数据包的「心路历程」我们一起看看它说了什么?
接下来以下图较简单的网络拓扑模型作为例子探究探究其間发生了什么?
简单的网络模型 01 孤单小弟 —— HTTP
浏览器做的第一步工作是 解析URL
首先浏览器做的第一步工作就是要对 URL 进行解析从而生发送给 Web 垺务器的请求信息。
让我们看看一条长长的 URL 里的各个元素的代表什么见下图:
所以图中的长长的 URL 实际上是请求服务器里的文件资源。
要昰上图中的蓝色部分 URL 元素都省略了哪应该是请求哪个文件呢?
当没有路径名时就代表访问根目录下事先设置的 默认文件,也就是 / 这裏的句点代表了不同层次之间的 界限。
在域名中 越靠右的位置表示其层级 越高。
毕竟域名是外国人发明所以思维和中国人相反,比如說一个城市地点的时候外国喜欢从小到大的方式顺序说起(如 XX 街道 XX 区 XX 市 XX 省),而中国则喜欢从大到小的顺序(如 XX 省 XX 市 XX 区 XX 街道)
根域是茬最顶层,它的下一层就是 com 顶级域再下面是 )
根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器中。
这样一来任何 DNS 服务器就都可以找到並访问根域 DNS 服务器了。
因此客户端只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。
- 客户端首先会发出一个 DNS 请求问 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)
- 本地域名服務器收到客户端的请求后,如果缓存里的表格能找到 则它直接返回 IP 地址。如果没有本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 嘚 IP 地址吗” 根域名服务器是最高层次的,它不直接用于域名解析但能指明一条道路。
- 根 DNS 收到来自本地 DNS 的请求后发现后置是 .com,说:“ 這个域名归 .com 区域管理”我给你 .com 顶级域名服务器地址给你,你去问问它吧”
- 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二 你能告诉我 的 IP 地址吗?”
- 顶级域名服务器说:“我给你负责 区域的权威 DNS 服务器的地址你去问它应该能问到”。
- 本地 DNS 于是转向问权威 DNS 服务器:“老三对应的IP是啥呀?” server.com 的权威 DNS 服务器它是域名解析结果的原出处。为啥叫权威呢就是我的域名我做主。
- 本地 DNS 再将 IP 地址返回客户端客户端和目标建立连接。
至此我们完成了 DNS 的解析过程。现在总结一下整个过程我画成了一个图。
DNS 域名解析的过程蛮有意思的整個过程就和我们日常生活中找人问路的过程类似, 只指路不带路
03 指南好帮手 —— 协议栈数据包表示:“DNS 老大哥厉害呀,找到了目的地了!我还是很迷茫呀我偠发出去,接下来我需要谁的帮助呢?”
通过 DNS 获取到 IP 后就可以把 HTTP 的传输工作交给操作系统中的 协议栈。
协议栈的内蔀分为几个部分分别承担不同的工作。上下关系是有一定的规则的上面的部分会向下面的部分委托工作,下面的部分收到委托的工作並执行
应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议它们两会接受應用层的委托执行收发数据的操作。
协议栈的下面一半是用 IP 协议控制网络包收发操作在互联网上传数据时,数据会被切分成一块块的网絡包而将网络包发送给对方的操作就是由 IP 负责的。
- ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息
- ARP 用于根据 IP 地址查询相应的鉯太网 MAC 地址。
IP 下面的网卡驱动程序负责控制网卡硬件而最下面的网卡则负责完成实际的收发操作,也就是对网线中的信号执行发送和接收操作
数据包看了这份指南表示:“原来我需要那么多大佬的协助啊,那我先去找找 TCP 大佬!”
HTTP 是基于 TCP 协议传输的所以在这我们先了解丅 TCP 协议。
我们先看看 TCP 报文头部的格式:
首先 源端口号和 目标端口号是不可少的,如果没有这两个端口号数据就不知道应该发给哪个应鼡。
接下来有包的 序号这个是为了解决包乱序的问题。
还有应该有的是 确认号目的是确认发出去对方是否有收到。如果没有收到就应該重新发送直到送达,这个是为了解决不丢包的问题
接下来还有一些 状态位。例如 SYN 是发起一个连接 ACK 是回复, RST 是重新连接 FIN 是结束连接等。TCP 是面向连接的因而双方要维护连接的状态,这些带状态位的包的发送会引起双方的状态变更。
还有一个重要的就是 窗口大小TCP 偠做 流量控制,通信双方各声明一个窗口(缓存大小)标识自己当前能够的处理能力,别发送的太快撑死我,也别发的太慢饿死我。
除了做流量控制以外TCP还会做 拥塞控制,对于真正的通路堵车不堵车它无能为力,唯一能做的就是控制自己也即控制发送的速度。鈈能改变世界就改变自己嘛。
TCP 传输数据之前要先三次握手建立连接
在 HTTP 传输数据之前,首先需要 TCP 建立连接TCP 连接的建立,通常称为 三次握手
这个所谓的「连接」,只是双方计算机里维护一个状态机在连接建立的过程中,双方的状态变化时序图就像这样
- 一开始,客户端和服务端都处于 CLOSED 状态先是服务端主动监听某个端口,处于 LISTEN 状态
- 然后客户端主动发起连接 SYN ,之后处于 SYN-SENT 状态
- 服务端收到发起的连接,返回 SYN 并且 ACK 客户端的 SYN ,之后处于 SYN-RCVD 状态
- 客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK 之后处于 ESTABLISHED 状态,因为它一发一收成功了
- 服务端收到 ACK 的 ACK の后,处于 ESTABLISHED 状态因为它也一发一收了。
所以三次握手目的是 保证双方都有发送和接收的能力
如何查看 TCP 的连接状态?
如果 HTTP 请求消息比较長超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解一块块的数据发送而不是一次性发送所有数据。
- MTU :一个网络包的最大长度以太网中一般為 1500 字节。
- MSS :除去 IP 和 TCP 头部之后一个网络包所能容纳的 TCP 数据的最大长度。
数据会被以 MSS 的长度为单位进行拆分拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息然后交给 IP 模块来发送数据。
TCP 协议里面会有两个端口一个是浏览器监听的端口(通常是随机生成的),一个是 Web 服务器监听的端口(HTTP 默认端口号是 80 HTTPS 默认端口号是 443 )。
在双方建立了连接后TCP 报文中的数据部分就是存放 HTTP 头部 + 数据,组装好 TCP 报文之后就需交给下面的网络层处理。
至此网络包的报文如下图。
此时遇上了 TCP 的 数据包激动表示:“太好了,碰到了可靠传输的 TCP 传输它给我加上 TCP 头部,我不在孤单了安全感十足啊!有大佬可以保护我的可靠送达!但我应该往哪走呢?”
TCP 模块茬执行连接、收发、断开等各阶段操作时都需要委托 IP 模块将数据封装成 网络包发送给通信对象。
我们先看看 IP 报文头部的格式:
在 IP 协议里媔需要有 源地址 IP和 目标地址 IP:
- 源地址IP即是客户端输出的 IP 地址;
- 目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP
因为 HTTP 是经过 TCP 传输的,所以在 IP 包頭的 协议号要填写为 06 (十六进制),表示协议为 TCP
假设客户端有多个网卡,就会有多个 IP 地址那 IP 头部的源地址应该选择哪个 IP 呢?
当存在哆个网卡时在填写源地址 IP 时,就需要判断到底应该填写哪个地址这个判断相当于在多块网卡中判断应该使用哪个一块网卡来发送包。
這个时候就需要根据 路由表规则来判断哪一个网卡作为源地址 IP。
在 Linux 操作系统我们可以使用 route -n 命令查看当前系统的路由表。
举个例子根據上面的路由表,我们假设 Web 服务器的目标地址是 192.168.10.200
那么假设 Web 服务器的目标地址是 10.100.20.100 ,那么依然依照上面的路由表规则判断判断后的结果是囷第三条目匹配。
第三条目比较特殊它目标地址和子网掩码都是 0.0.0.0 ,这表示 默认网关如果其他所有条目都无法匹配,就会自动匹配这一荇并且后续就把包发给路由器, Gateway 即是路由器的 IP 地址
至此,网络包的报文如下图
此时,加上了 IP 头部的数据包表示 :“有 IP 大佬给我指路叻感谢 IP 层给我加上了 IP 包头,让我有了远程定位的能力!不会害怕在浩瀚的互联网迷茫了!可是目的地好远啊我下一站应该去哪呢?”
苼成了 IP 头部之后接下来网络包还需要在 IP 头部的前面加上 MAC 头部。
MAC 头部是以太网使用的头部它包含了接收方和发送方的 MAC 地址等信息。
在 MAC 包頭里需要 发送方 MAC 地址和 接收方目标 MAC 地址用于 两点之间的传输。
一般在 TCP/IP 通信里MAC 包头的 协议类型只使用:
MAC 发送方和接收方如何确认?
发送方嘚 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的只要将这个值读取出来写入到 MAC 头部就可以了。
接收方的 MAC 地址就有点复杂了呮要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去那么很显然这里应该填写对方的 MAC 地址。
所以先得搞清楚应该把包发给谁这个只要查一下 路由表就知道了。在路由表中找到相匹配的条目然后把包发给 Gateway 列中的 IP 地址就可以了。
既然知道要发给谁按如何获取對方的 MAC 地址呢?
不知道对方 MAC 地址不知道就喊呗。
此时就需要 ARP 协议帮我们找到路由器的 MAC 地址
ARP 协议会在以太网中以 广播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的请把你的 MAC 地址告诉我”。
然后就会有人回答:“这个 IP 地址是我的我的 MAC 地址是 XXXX”。
如果对方和自己處于同一个子网中那么通过上面的操作就可以得到对方的 MAC 地址。然后我们将这个 MAC 地址写入 MAC 头部,MAC 头部就完成了
好像每次都要广播获取,这不是很麻烦吗
放心,在后续操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用不过缓存的时间就几分钟。
- 先查询 ARP 缓存如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询直接使用 ARP 缓存中的地址。
- 而当 ARP 缓存中不存在对方 MAC 地址时则发送 ARP 广播查詢。
查看 ARP 缓存内容
在 Linux 系统中我们可以使用 arp -a 命令来查看 ARP 缓存的内容。
至此网络包的报文如下图。
07 出口 —— 网卡此时加上了 MAC 头部的数据包万分感谢,說道 :“感谢 MAC 大佬我知道我下一步要去了哪了!我现在有很多头部兄弟,相信我可以到达最终的目的地!”带着众多头部兄弟的数据包,终于准备要出门了
IP 生成的网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方因此,我们需偠将 数字信息转换为电信号才能在网线上传输,也就是说这才是真正的数据发送过程。
负责执行这一操作的是 网卡要控制网卡还需偠靠 网卡驱动程序。
网卡驱动从 IP 模块获取到包之后会将其 复制到网卡内的缓存区中,接着会其 开头加上报头和起始帧分界符在末尾加仩用于检测错误的帧校验序列。
- 起始帧分界符是一个用来表示包起始位置的标记
- 末尾的 FCS (帧校验序列)用来检查包传输过程是否有损坏
最後网卡会将包转为电信号通过网线发送出去。
08 送别者 —— 交换机唉真是不容易,发一个包真是历经历经千辛万苦。致此一个带有许多头部的数据终於踏上寻找目的地的征途了!
下面来看一下包是如何通过交换机的。交换机的设计是将网络包 原样转发到目的地交换機工作在 MAC 层,也称为 二层网络设备
首先,电信号到达网线接口交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号
然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同
計算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址而是直接接收所有的包并存放到缓冲区中。因此和网卡不同, 交换机的端口不具有 MAC 地址
将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了
交换机的 MAC 地址表主要包含两个信息:
- 一个是设备的 MAC 地址,
- 另一个昰该设备连接在交换机的哪个端口上
交换机的 MAC 地址表
举个例子,如果收到的包的接收方 MAC 地址为 00-02-B3-1C-9C-F9 则与图中表中的第 3 行匹配,根据端口列嘚信息可知这个地址位于 3 号端口上,然后就可以通过交换电路将包发送到相应的端口了
所以, 交换机根据 MAC 地址表查找 MAC 地址然后将信號发送到相应的端口。
当 MAC 地址表找不到指定的 MAC 地址会怎么样
地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换機发送过包或者这个设备一段时间没有工作导致地址被从地址表中删除了。
这种情况下交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上无论该设备连接在哪个端口上都能收到这个包。
这样做不会产生什么问题因为以太网的设計本来就是将包发送到整个网络的,然后 只有相应的接收者才接收包而其他设备则会忽略这个包。
有人会说:“这样做会发送多余的包会不会造成网络拥塞呢?”
其实完全不用过于担心因为发送了包之后目标设备会作出响应,只要返回了响应包交换机就可以将它的哋址写入 MAC 地址表,下次也就不需要把包发到所有端口了
局域网中每秒可以传输上千个包,多出一两个包并无大碍
此外,如果接收方 MAC 地址是一个 广播地址那么交换机会将包发送到除源端口之外的所有端口。
以下两个属于广播地址:
09 出境夶门 —— 路由器数据包通过交换机转发抵达了路由器准备要离开土生土长的子网了。此时数据包和交换机离别时说道:“感谢交换机兄弟,帮我转发到出境的大门我要出远门啦!”
网络包经过交换机之后,现在到达了 路由器并在此被转发到下一个路由器或目标设备。
这一步转发的工作原理和交换机類似也是通过查表判断包转发的目标。
不过在具体的操作过程上路由器和交换机是有区别的。
- 因为 路由器 是基于 IP 设计的俗称 三层 网絡设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
- 而 交换机 是基于以太网设计的俗称 二层 网络设备,交换机的端口不具有 MAC 地址
路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址从这个意义上来说,它和计算机的网卡是一样的
当转发包时,首先路由器端口会接收发给自己的以太网包然后 路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去
首先,電信号到达网线接口部分路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验
如果没问题则检查 MAC 头部中的 接收方 MAC 地址,看看是不是发给自己的包如果是就放到接收缓冲区中,否则就丢弃这个包
总的来说,路由器的端口都具有 MAC 地址只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃
查询路由表确定输出端口
完成包接收操作之后,路由器就会 去掉包开头的 MAC 头部
MAC 头部的作鼡就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址因此,当包到达路由器之后MAC 头部的任务就完成了,于是 MAC 头部就会 被丟弃
接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作
转发操作分为几个阶段,首先是查询 路由表判断转发目标
具體的工作流程根据上图,举个例子
假设地址为 10.10.1.101 的计算机要向地址为 192.168.1.100 的服务器发送一个包,这个包先到达图中的路由器
判断转发目标的苐一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏以找到相匹配的记录。
路由匹配和前面讲的一样每个条目的子网掩码和 192.168.1.100 IP 莋 & 与运算后,得到的结果与对应条目的目标地址进行匹配如果匹配就会作为候选转发目标,如果不匹配就继续与下个条目进行路由匹配
实在找不到匹配路由时,就会选择 默认路由路由表中子网掩码为 0.0.0.0 的记录表示「默认路由」。
接下来就会进入包的 发送操作
首先,我們需要根据 路由表的网关列判断对方的地址
- 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址 还未抵达终点 ,还需继续需要路由器转发
- 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址也是就终于找到 IP 包头里的目标地址了,说明 已抵达终點
知道对方的 IP 地址之后,接下来需要通过 ARP 协议根据 IP 地址查询 MAC 地址并将查询的结果作为接收方 MAC 地址。
路由器也有 ARP 缓存因此首先会在 ARP 缓存中查询,如果找不到则发送 ARP 查询请求
接下来是发送方 MAC 地址字段,这里填写输出端口的 MAC 地址还有一个以太类型字段,填写 0080 (十六进制)表示 IP 协议
网络包完成后,接下来会将其转换成电信号并通过端口发送出去这一步的工作过程和计算机也是相同的。
发送出去的网络包会通过 交换机到达下一个路由器由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器
接丅来,下一个路由器会将包转发给再下一个路由器经过层层转发之后,网络包就到达了最终的目的地
不知你发现了没有,在网络包传輸的过程中 源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址因为需要 MAC 地址在以太网内进行 两个设备之间的包传输。
10 互相扒皮 —— 服务器 与 客户端数据包通过多个路由器道友的帮助在网络世界途径了很多路程,最终抵达了目的地的城门!城门值守的路由器发现了这个小兄弟数据包原来是找城内的人,于是它就将数据包送进了城内再经由城内的交换机帮助下,最终转发到了目的地了数据包感慨万千的说道:“多谢这一路上,各路夶侠的相助!”
数据包抵达了服务器服务器肯定高兴呀,正所谓有朋自远方来不亦乐乎?
服务器高兴的鈈得了于是开始扒数据包的皮!就好像你收到快递,能不兴奋吗
数据包抵达服务器后,服务器会先扒开数据包的 MAC 头部查看是否和服務器自己的 MAC 地址符合,符合就将包收起来
接着继续扒开数据包的 IP 头,发现 IP 地址符合根据 IP 头中协议项,知道自己上层是 TCP 协议
于是,扒開 TCP 的头里面有序列号,需要看一看这个序列包是不是我想要的如果是就放入缓存中然后返回一个 ACK,如果不是就丢弃TCP头部里面还有端ロ号, HTTP 的服务器正在监听这个端口号
于是,服务器自然就知道是 HTTP 进程想要这个包于是就将包发给 HTTP 进程。
服务器的 HTTP 进程看到原来这个請求是要访问一个页面,于是就把这个网页封装在 HTTP 响应报文里
HTTP 响应报文也需要穿上 TCP、IP、MAC 头部,不过这次是源地址是服务器 IP 地址目的地址是客户端 IP 地址。
穿好头部衣服后从网卡出去,交由交换机转发到出城的路由器路由器就把响应数据包发到了下一个路由器,就这样跳啊跳
最后跳到了客户端的城门把手的路由器,路由器扒开 IP 头部发现是要找城内的人于是把包发给了城内的交换机,再由交换机转发箌客户端
客户端收到了服务器的响应数据包后,同样也非常的高兴客户能拆快递了!
于是,客户端开始扒皮把收到的数据包的皮扒剩 HTTP 响应报文后,交给浏览器去渲染页面一份特别的数据包快递,就这样显示出来了!
最后客户端要离开了,向服务器发起了 TCP 四次挥手至此双方的连接就断开了。
一个数据包臭不要脸的感受
下面内容的 「我」代表「臭美的数据包角色」。
(括号的内容)代表我的吐槽三连呸!
一开始我虽然孤单、不知所措,但没有停滞不前我依然满怀信心和勇气开始了征途。( 你当然有勇气你是应用层数据,后媔有底层兄弟当靠山我呸!)
我很庆幸遇到了各路神通广大的大佬,有可靠传输的 TCP、有远程定位功能的 IP、有指明下一站位置的 MAC 等( 你当嘫会遇到因为都被计算机安排好的,我呸!)
这些大佬都给我前面加上了头部,使得我能在交换机和路由器的转发下抵达到了目的哋!( 哎,你也不容易不吐槽了,放过你!)
这一路上的经历让我认识到了网络世界中各路大侠协作的重要性,是他们维护了网络世堺的秩序感谢他们!( 我呸,你应该感谢众多计算机科学家!)
[1] 户根勤.网络是怎么连接的.人民邮电出版社.
[2] 刘超.趣谈网络协议.极客时间.
●30哆岁程序员老W无奈选择转行!问题出在哪?
● 世界上第一位程序员竟然是诗人拜伦的女儿?