一个数据包的网络旅程
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
一切从按下回车键开始。
在浏览器这里使用的是HTTP协议,所以在应用层就是要封装一个HTTP数据包,那么看一下HTTP报文长什么样子,需要填写什么内容。
需要上面这些内容才能形成一个完整的HTTP报文,那么浏览器就要开始寻找和填充这些内容了。
URL的组成:协议名://服务器域名/目录名/文件名
通过解析URL就知道了HTTP报文中的请求行需要的内容。
当URL中没有路径名时,就代表访问根目录下事先设置的默认文件,也就是
/index.html
或者/default.html
这些文件,这样就不会发生混乱了。
剩下的HTTP Header和Body浏览器也会自己组装好,这样HTTP报文就打包完成了。
HTTP协议在传输层使用的是TCP协议。TCP报文中需要什么内容呢?
需要源端口号和目的端口号,Web服务器使用HTTP协议时,默认的端口号时80,使用HTTPS时,默认的端口号时443这两个就是目的端口号,浏览器会随机的选择一个本机不在监听的端口作为源端口号与目的端口进行通信。
因为TCP协议是面向连接的协议,在建立连接的过程中需要握手,所以就需要知道服务器的IP地址,但是在浏览器的地址栏中输入的时域名,因此需要域名解析。
解析域名需要使用DNS。
域名都是有层级关系的,DNS中的域名都是用英文的句号来分隔的,句号就是不同层级之间的界限。在域名中,越靠近右边的位置,表示它的层级越高。
所有域名的层级关系类似一个树状结构:
根 DNS 服务器
顶级域 DNS 服务器(com)
权威 DNS 服务器(server.com)
根域的 DNS 服务器的信息保存在互联网中所有的 DNS 服务器中。这样一来,任何 DNS 服务器就都可以找到并访问根域 DNS 服务器了。因此,浏览器只要能够找到任意一台 DNS 服务器,就可以通过它找到根域 DNS 服务器,然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。
DNS解析域名的过程是一个递归的过程。
向本地DNS服务器发起一个DNS请求
本地DNS服务器中有该DNS请求的缓存则直接返回IP地址,否则本地DNS服务器会去询问根域名服务器
根域名服务器收到本地DNS服务器的请求后,返回该DNS请求中域名对应的顶级域名服务器的地址
本地DNS服务器再向顶级域名服务器发起请求
顶级域名服务器收到本地DNS服务器的请求后,返回该DNS请求中域名对应的权威域名服务器的地址
本地DNS服务器再向权威服务器发起请求
权威服务器收到本地DNS服务器的请求后,返回该域名对应的IP地址
本地DNS服务器将这个IP地址返回给浏览器
现在有了IP地址了,TCP协议就可以开始建立连接。
一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。
服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。
客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。
服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。
所以三次握手目的是保证双方都有发送和接收的能力。
如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解一块块的数据发送,而不是一次性发送所有数据。
数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。
MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。
在双方建立了连接后,TCP 报文中的数据部分就是存放 HTTP 头部 + 数据,组装好 TCP 报文之后,就需交给下面的网络层处理。
TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。
IP报文中需要保存哪些内容呢?
在 IP 协议里面需要有源地址 IP 和 目标地址 IP:
源地址IP,即是客户端输出的 IP 地址;
目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。
因为 HTTP 是经过 TCP 传输的,所以在 IP 包头的协议号,要填写为 06(十六进制),表示协议为 TCP。
当存在多个网卡时,在填写源地址 IP 时,就需要判断到底应该填写哪个地址(相当于在多块网卡中判断应该使用哪个一块网卡来发送包)。这个时候就需要根据路由表规则,来判断哪一个网卡作为源地址 IP。
在 Linux 操作系统,使用 route -n
命令查看当前系统的路由表。
假设 Web 服务器的目标地址是 192.168.10.200。路由表的内容如下所示:
首先先和第一条条目的子网掩码(Genmask)进行 与运算,得到结果为 192.168.10.0,但是第一个条目的 Destination 是 192.168.3.0,两者不一致所以匹配失败。
再与第二条目的子网掩码进行 与运算,得到的结果为 192.168.10.0,与第二条目的 Destination 192.168.10.0 匹配成功,所以将使用 eth1 网卡的 IP 地址作为 IP 包头的源地址。
假设 Web 服务器的目标地址是 10.100.20.100,那么依然依照上面的路由表规则判断,判断后的结果是和第三条目匹配。
第三条目比较特殊,它目标地址和子网掩码都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器,Gateway 即是路由器的 IP 地址。
生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部。
MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。
在 MAC 包头里需要发送方 MAC 地址和接收方目标 MAC 地址,用于两点之间的传输。
一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:
0800 :IP 协议
0806 :ARP 协议(用于根据 IP 地址查询相应的以太网 MAC 地址)
ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。
发送方的 MAC 地址获取就比较简单了,MAC 地址是在网卡生产时写入到 ROM 里的,只要将这个值读取出来写入到 MAC 头部就可以了。
接收方的 MAC 地址就有点复杂了,只要告诉以太网对方的 MAC 的地址,以太网就会帮我们把包发送过去,那么很显然这里应该填写对方的 MAC 地址。
所以先得搞清楚应该把包发给谁,这个只要查一下路由表就知道了。在路由表中找到相匹配的条目,然后把包发给 Gateway 列中的 IP 地址就可以了。
不知道MAC地址时,使用ARP协议来查找路由器的MAC地址。ARP 协议会在以太网中以广播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的?请把你的 MAC 地址告诉我”。
操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟。在 Linux 系统中,我们可以使用
arp -a
命令来查看 ARP 缓存的内容。
因为数据包要再网络中通过很多次跳转才能到服务器,所有一般都是写途径的路由器的MAC地址,一步一步的往下路由。在这里只要保证再一个局域网内,也就是一个路由设备之内,每一个MAC地址是唯一的就行了。
IP 生成的网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,需要将数字信息转换为电信号,才能在网线上传输,这才是真正的数据发送过程。
负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序。网卡驱动从 IP 模块获取到包之后,会将其复制到网卡内的缓存区中,接着会再开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。
这样就形成了物理层的数据包:
起始帧分界符是一个用来表示包起始位置的标记
末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏
最后网卡会将包转为电信号,通过网线发送出去。
交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备。
首先,电信号到达网线接口,交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号。然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。
计算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有 MAC 地址。将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。
交换机的 MAC 地址表主要包含两个信息:
一个是设备的 MAC 地址
另一个是该设备连接在交换机的哪个端口上
交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口。
地址表中找不到指定的 MAC 地址。这可能是因为具有该地址的设备还没有向交换机发送过包,或者这个设备一段时间没有工作导致地址被从地址表中删除了。这种情况下,交换机无法判断应该把包转发到哪个端口,只能将包转发到除了源端口之外的所有端口上,无论该设备连接在哪个端口上都能收到这个包。这样做不会产生什么问题,因为以太网的设计本来就是将包发送到整个网络的,然后只有相应的接收者才接收包,而其他设备则会忽略这个包。
网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。不过在具体的操作过程上,路由器和交换机是有区别的。
因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。
路由器的端口具有 MAC 地址,因此能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。当转发包时,首先路由器端口会接收发给自己的以太网包,然后路由表查询转发目标,再由相应的端口作为发送方将以太网包发送出去。
首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。
如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。
路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。
完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。
MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃。
转发操作分为几个阶段,首先是查询路由表判断转发目标。
判断转发目标的第一步,就是根据包的接收方 IP 地址查询路由表中的目标地址栏,以找到相匹配的记录。
接下来就会进入包的发送操作。首先,我们需要根据路由表的网关列判断对方的地址。
如果网关是一个 IP 地址,则这个IP 地址就是要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。
如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点。
知道对方的 IP 地址之后,通过 ARP 协议根据 IP 地址查询 MAC 地址,并将查询的结果作为接收方 MAC 地址。
根据MAC地址封装网络数据包,将其转换成电信号并通过端口发送出去。
继续递归的进行拆包封包转发操作,数据包最终到达目的地,服务器一层一层的拆包获取里面的HTTP数据报文。
在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。