在浏览器输入网址后,发生了什么?
当我们的电脑(主机)连接到网络后,就已经获得了主机的IP地址、DNS服务器的IP地址。之后,当我们打开浏览器,输入网址www.baidu.com并按下回车键,都发生了什么呢?
虽然百度最近在走下坡路,但它目前仍是被ping网站的首选。
发起DNS查询
要想与百度的服务器通信,需要与服务器建立TCP连接。建立连接前,需要知道百度服务器的IP地址,这是通过DNS查询完成的。为此,主机向DNS服务器发送DNS查询报文。
ARP获取路由器的MAC地址
发送此报文前,还需要通过ARP查询报文获取路由器的MAC地址,通过设置目的MAC地址为
FF:FF:FF:FF:FF:FF
,使包在局域网中广播,交换机将此报文转发给所有连接的设备,路由器接收此报文后,回复ARP响应报文,最后通过交换机到达主机。
DNS服务器中有一张表,它记录了域名与IP地址的对应关系:
域名 | Class | 记录类型 | 响应数据 |
www.baidu.com | IN | A | 5.6.7.8 |
www.pighub.com | IN | A | 1.2.3.4 |
在上表中,Class
是一个由于历史原因而保留的兼容项,它的值永远是IN
;记录类型有不同的取值,例如A表示IP地址,MX表示邮件服务器等。
用户向DNS服务器发送查询请求(www.baidu.com, IN, A)
:
上面查找的IP地址是IPv4地址。事实上,还会发起一个IPv6地址的查询:
它的查询类型为AAAA.
DNS服务器在表中查询相关记录,在这里匹配到了表的第一条记录,返回对应的IP地址。
如果DNS服务器中不存在www.baidu.com的记录,该怎么办?
一个DNS服务器不可能装下世界上所有的域名和IP地址,如果不存在相关记录,会向上一层的DNS服务器发起查询请求,很像函数的递归调用。
域名通过点号分层,例如www.baidu.com表示baidu.com是com下的一个子域,在com之上还有一个最顶层的DNS服务器(根域DNS服务器)。DNS服务器按照上下层的关系建立联系。下层DNS服务器的IP地址保存在上层DNS服务器中,DNS服务器可以发送查询请求到根DNS服务器,再从根DNS服务器一路向下,直到待查询的域名。
当DNS服务器查找到www.baidu.com的IP地址后,在表中缓存这条记录,并设置一个有效期限,超过有效期限后,将移除这条记录。
以上两个DNS查询都返回了查询结果。由于我接入的网络支持IPv6, 之后也是通过IPv6地址与百度服务器连接的。下面是DNS查询www.baidu.com
IPv6地址的结果:
建立TCP连接
TCP协议
TCP协议保证数据的可靠传输,此协议主要包括以下几个模块:
- 基本信息:(发送方IP地址、发送方端口号)、(接收方IP地址、接收方端口号);
- 信息完整性:序号(当前发送数据的偏移)、ACK号(接收方已经接收到的数据);
- 控制位:ACK、SYN(用于连接)、FIN(用于断开连接)、RST、URG、PSH;
- 负载均衡:窗口大小(当前缓冲区剩余的内存大小)
TCP的连接过程被形象地称为“3次握手”:
1. 主机请求连接,SYN=1.
上图中,主机的7970端口与百度服务器的443号端口通信(因为是https连接)。
Sequence Number(raw)
是主机随机生成的一个初始序号,在这里是1585906936. 在Flags中,SYN被置为1. Window表示主机端的窗口大小。
2. 百度服务器回复响应.
首先,Flags中SYN=1, ACK=1. 返回的ACK号(Acknowledgment number (raw)
)为1585906937 = 1585906936 + 1.
为什么ACK号+1?
有种很形象的说法,+1表示接收到了主机发送的1个比特,即控制位SYN.
另外,服务器也发送了初始序列号1695001808. 告知主机其窗口大小为8192.
3. 主机端回复确认.
这里,Flags的ACK=1,ACK号为1695001809 = 1695001808 + 1,和之前是同样的道理。服务器在收到这个包后,完成连接。
如果服务器没有收到客户端的ACK,会发生什么?
服务器会在一段时间后清除为此连接分配的内存,连接失败。
为什么会有3次握手?按我的理解,如果A、B需要通信,就必须测试从A到B、从B到A这两条线路是否是通的。每次测试包括一来一回,按理说应该需要4次,即:A发送给B、B返回确认给A、B发送给A、A返回确认给B. 只不过,B返回确认给A和B发送给A这两件事情可以合在一起做,所以最后是3次握手,而不是4次。
TLS密码交换
当前网站基本上都是https连接,它在http的基础上增加了数据加密功能,这个过程比较复杂。
1. 加密算法有很多,因此,主机和服务器需要确定使用何种加密算法。首先,主机向服务器发送可用的加密算法清单:
另外,客户端还会生成一个随机数,用于之后生成加密密钥。
2. 服务器从清单中选择一个并回复主机。这样,二者确认了共同的加密算法:
同时,服务器也会生成一个随机数。
3. 确认使用的加密算法后,服务器与客户端交换密钥。这里使用的是Diffie-Hellman密钥交换算法:
- 服务器生成算法所需的参数(质数p、基数g、服务器公钥A)并发送到主机;
- 主机收到服务器的参数后,生成公钥B,并发送给服务器。
- 此时,主机和服务器各自拥有对方的公钥和自己的私钥,可通过算法计算出一个共享的密钥(Pre-Master Secret);
- 主机和服务器使用之前交换的随机数和Pre-Master Secret,生成主密钥(Master Secret)。有了主密钥后,双方使用对称加密算法交换信息。
以上步骤是通过交换几个包完成的,在此不再细说。
发送HTTP请求报文
现在万事俱备,可以向百度的服务器发送HTTP请求报文:
GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br, zstd
DNT: 1
......
第一行的GET表示请求方法,意味着从服务器获取内容。除了GET外,最常见的是POST,表示向服务器发送数据。除此之外,还有OPTIONS,PUT,DELETE等不太常见的选项。最后是HTTP的版本,这里是HTTP/1.1.
接下来是一系列的消息头,它由字段名和字段值组成。如果是POST方法,在消息头后还会携带需要发送的数据。
服务器收到上述请求后,找到请求的网页并返回:
HTTP/1.1 200 OK
Connection: keep-alive
......
[html网页]
这里的200表示请求成功,OK则是对此状态码的描述。
状态码
最常见的状态码必然是404,404 NOT FOUND表示在服务器上找不到请求的资源。常见的状态码及含义如下:
- 2XX:成功
- 3XX:需要进一步操作
- 4XX:客户端错误
- 5XX:服务器错误
返回的网页中包含图片等其它链接,按照上述步骤通过TCP连接请求即可。最后,一个完整的网页被渲染在我们面前!
补充:IP层
主机发送的包会被转发到最近的设备(路由器、集线器等),此设备根据头部信息确认接下来发送的位置。IP协议负责确认下一个IP转发设备,即路由器,MAC协议负责将包传到下一个转发设备。
IP模块负责添加IP头部和MAC头部风格,封装好的包被交给网卡。网卡将数字信息转化为电信号或光信号,通过网线或光纤发送出去,从而到达集线器、路由器等转发设备。之所以这样设计,是因为IP和以太网的部分都可以替换为其它协议,例如以太网可以替换为无线局域网、ADSL、FTTH等。将IP和负责传输的网络分离开来,可以更好地按需使用各种通信技术。
集线器
网络包在传输过程中会经过集线器,集线器中有一张表,这张表根据以太网头部记录的目的地信息确定转发方向。
IP头部
IP协议根据IP头部中的IP地址确定下一个路由器的位置,IP头部包含如下内容:
- 基本信息:版本号、发送方、接收方IP地址、头部长度、服务类型、总长度、协议号
- 分片功能:是否允许分片、当前是否为分片包、分片ID号(被分片的IP包具有相同的ID号)、分片偏移量
- 避免死循环:生存时间TTL(每经过一个路由器,TTL减1,减到0时,包被丢弃)
路由器
交换机是基于以太网设计的,而路由器是基于IP设计的。路由器具有转发和端口两个模块。在转发模块中,路由器根据其中的路由表,判断包的转发目标。路由器的转发模块可具有多个支持各类协议的转发模块,例如以太网、ADSL、FTTH等。
计算机的网卡大多只支持以太网和无线局域网,而路由器的端口模块支持多种通信技术,如ADSL、FTTH、宽带专线等。
路由器首先从端口接收包(以太网的包、无线局域网的包等等),根据包中IP地址在路由表中查询,确定转发端口并发送出去。路由器的各个端口都有自己的MAC地址和IP地址。
一个路由表如下图所示:
目标地址 | 子网掩码 | 网关 | 接口 | 跃点数 |
10.10.1.0 | 255.255.255.0 | / | e2 | 1 |
0.0.0.0 | 0.0.0.0 | 192.0.2.1 | e1 | 1 |
其中,目标地址为接收方的信息。路由聚合功能会将几个子网合并为一个子网,在路由表中只产生一条记录。网关是包的转发目标,当匹配到某条记录后,路由器将包转到指定的网络接口,并转发到网关列中指定的IP地址。跃点计数表示距离目标IP地址的远近。
路由器接收到包后,MAC头部被抛弃。如果在路由表中找不到匹配的条目,路由器会丢弃这个包,并通过ICMP告知发送方。路由表的最后一行是0.0.0.0, 所有未匹配上的都会匹配此条目,网络包会被转发到互联网接入路由器,这条记录也被称为默认路由,这条记录配置的网关地址被称为默认网关。
分片
路由器的端口支持其它局域网或专线通信技术,不同技术允许的最大包长度不同。为此,可通过分片功能拆分包。
如果这个包的不能分片位=1,只能丢弃这个包,并通过ICMP消息通知发送方。否则将其分片。
补充:MAC层
MAC模块在开头加上报头和起始帧分界符、在末尾加上用于错误检测的帧校验序列。MAC头部包括发送方、接收方的MAC地址、以太类型(IP, ARP等)。
接收方的MAC地址通过ARP协议查询,通过把包发送到同一以太网的所有设备,如果对方和自己处于同一个子网,就会得到对方的MAC地址。ARP表会缓存查找的MAC地址几分钟。
补充:物理层
当数据包被发送到网卡时,网卡将数字信号转化为电信号。当信号到达集线器后,被广播到所有设备,设备根据接收方的MAC地址判断应该接受哪些包。
首先,信号到达网线接口,电信号转化为通用格式,并传递给MAC模块,MAC模块将信号转化为数字信息,并利用包末尾的FCS校验错误,如果没有问题,则将包放到缓冲区。
交换机内部有一张MAC地址与网线端口的对应表,接收到包时,会将包中发送方MAC地址和接收到的端口号写入表中,从而根据地址判断设备连接在哪个端口上。接下来,查询包的接收方MAC地址是否在表中,如果存在,则转发到对应的端口。
另外,MAC地址表会在一段时间后,将记录从表中删除。如果交换机发现某个包需要发送到它来的那个端口,这说明发送方和接收方处于同一个集线器下,那么交换机会丢弃这个包。如果地址表中找不到指定的MAC地址,交换机只能把包发送到除源端口外的所有端口上,以保证此设备连接在哪个端口上,都能接收到这个包。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步