思考:浏览器打开网站经历了些什么过程?
笔者将其解释为以下几个步骤:
- 域名解析
- TCP会话建立
- 数据传输
- TCP会话结束
其实浏览器访问网站的整个过程实质上是资源获取的过程。
换个说法,浏览器展示的所有内容都是资源。也就是网站服务商提供的一系列资源,因此访问网站实质上就是访问服务器上的资源。
这个资源可以是图片、可以是html文档、可以是js或者css、当然还包括数据库内的数据等等。
补充说明:
1.1:我们把自己正在使用的计算机记作客户端 Client,存放资源的计算机记作 Server。C与S之间的通讯遵循TCP/IP模型。
可以理解为从插入网线(或者接入Wifi)开始,我们的计算机C与任意计算机X之间的所有数据交互都必须同样的网络协议。
把这个概念再放大一点,网络设备之间的通讯都是遵循TCP/IP模型。
1.2:整个互联网的应用,也都是基于这个协议栈来实现的。可以将其理解为房屋的地基。
1.3:我们把地基之上所提供的服务称之为应用层服务。包括(HTTP、DNS、Telnet、SSH、LDAP、SMTP等)
假设我们要访问 example 这个网站,打开浏览器输入 https://www.example.com:443/,浏览器上就能看到example网站的首页。
补充说明:
1.1: 在因特网中,公网IP地址才可以在互联网中进行通讯(局域网就无所谓了,局域网的IP地址可以用私有地址,也可以用公有地址通信)。
example.com这个网站的服务器肯定不在我家里组局域网,所以我要访问它,那么我们两台计算机只能在公网进行通讯,我们各自都会有一个公网IP地址。
1.2: 当我们拿到example的公网IP后,路由器(网关)通过寻址协议(EGP、BGP、IGP、RIP、OSPF),我们的计算机能得到一条能通往这台example服务器的链路。
1.3: 最后路由器根据MAC地址与IP的映射关系转发数据给对应的计算机。MAC地址不用来在互联网中寻址,它是最后交换机这些设备用来转发数据包的。(可参考ARP协议)
关于路由寻址的相关内容大家可以百度详细的图解说明。
1.域名解析
计算机首先进行域名解析, 将域名解析成IP有以下三种方式。为什么网站经常用域名访问,因为IP地址太难记忆了。
以下三种方式按照顺序a\b\c执行,如果第一步就得到IP地址,那么无需进行后续步骤。
a: 计算机本地hosts文件 (Linux 路径: /etc/hosts ;Windows 路径: c\system32\drivers\etc\hosts)
b: 本地缓存 (笔者猜测操作系统缓存、浏览器缓存)
c: DNS服务
DNS服务器是分布式的系统,我们也可以把它理解为应用层的一个服务。是帮助计算机获取域名对应IP地址的服务。
这里需要注意,如果流量被运营商劫持了,由运营商自己的DNS告诉你IP,就不一定是下面几个步骤了。
其中通过DNS服务器拿到IP地址最为复杂,笔者大致罗列了下其中过程。步骤记作c1-c4
c1: DNS resolver (DNS递归解析服务器)
c2: DNS root name server (DNS根域名解析服务器)
c3: name server for .com TLD (TLD域名解析服务器)
c4: Amazon Route 53 name server (权威域名解析服务器)
上述四个步骤,可以理解为
客户端--->C1:客户端向C1取域名对应的IP地址缓存,有就返回IP,没有C1问C2要。
C1--->C2:C2管理所有TLD服务器的信息,C2告诉C1问具体询问哪台TLD服务器
C1--->C3:C3告诉客户端问哪台权威域名服务,这台权威服务器就有baidu.com 相关的IP地址
C1--->C4:C4告诉C1 baidu.com 域名所对应的IP地址
最后C1告诉客户端此域名examle对应的IP是多少,这四种解析服务器协同作业完成域名到IP的解析。
补充说明:
DNS使用53端口,TCP/UDP协议均有使用。(DNS服务器之间使用TCP,客户端与递归服务器之间使用UDP。)
使用wiresharek 过滤 dns.query.name == "www.baidu.com" 抓包即可查看到DNS协议。
最后得的IP地址
192.168.1.1 www.baidu.com
以上是笔者总结的大致流程,有兴趣大家自信百度更详细的图解说明。
用浏览器打开百度,wireshark抓包结果。我这里把DNS服务器配成了114
image.png
image.png
在cmd里面输入 nslookup www.baidu.com 结果也是一样的
这里浏览器用无论用哪个IP地址都能访问到百度
1.TCP会话建立
我们通过上述操作得到了百度服务器的公网IP,这都是无感知操作。
剩下的就是与目标服务器进行沟通,上面也说了计算机之间沟通都遵循TCP/IP协议栈。(路由寻址也是使用的TCP/IP模型,只是使用的层度不同而已。)
Web服务主要使用HTTP协议, HTTP协议是基于TCP的协议。
补充说明:
IP地址定位到计算机,端口定位到计算机某个进程。
我们把计算机正在运行的所有程序想象成大别墅里的各个小房间,房间里的窗户就是互联网上各不同计算机之间进程沟通的渠道。
TCP会话的创建需要三次握手(握手关键字TCP标志位 SYN, ACK)。此操作完成后,客户端与服务端已能进行通信。
https 协议是 http + tls,因此TCP会话完成后会立即进行tls的握手与证书的校验。
下面是TCP的三次握手以及TLS协议的握手抓包
image.png
补充说明:
tls目前主要使用tls1.2,tls1.0和tls1.1在2020年左右基本上就不建议使用了。
目前tls1.3版本已经出了,新版本的浏览器基本上都支持。1.3版本主要是更加安全(密码套件精简,其中支持的哈希算法更复杂),握手次数更少,
支持前向加密(利用DH算法交换密钥),也就是对每次数据都加密。(这样有一个好处,就算密钥泄露一次也无法对历史数据进行解密。)
tls1.2握手流程大致如下,假设客户端C,服务端S
C--->S client hello 客户端向服务端发送消息,其中包括随机数x、支持的加密算法、协议版本等信息
S--->C server hello 服务端选择一个加密算法和协议版本、随机数y等进行响应
S--->C Certificate, server hello done 服务端继续发送一个包含其公钥的整数,以便客户端验证服务器身份,并用证书内服务器的公钥加密 随机数 z
C--->S client key Exchange, change cipher spec, encrypted handshake message
客户端使用证书内的公钥加密一个临时密钥,并告诉服务端现在开始使用新的加密参数,客户端使用协商的加密算法对消息进行加密。最后发送给服务端
S--->A change cipher spec, encrupted handshake message
服务端告知客户端从现在开始使用新的加密参数,并使用协商的加密算法对消息进行加密,然后发送给客户端
客户端与服务端通过 encrupted handshake message消息完成握手过程。
此处不再使用SSL举例,TLS本就是SSL的继任者。二者最终目的都一致。
以上是笔者总结的大致流程,有兴趣大家百度更详细的图解说明。
3.数据传输
TLS的会话已经建立完成,那么后续就是发送和接收HTTP协议的数据了。
http协议有很多版本,目前使用较多的几个版本:
http/1.0 较老,使用的并非很多,不支持keep-alive
http/1.1 比较常见,支持keep-alive
http/2.0 比较常见,协议压缩,传输模型调整,性能效率更高,服务端主动推送相关数据
http/3.0 实际应用中并不普及,下一代HTTP协议,传输层使用QUIC协议(基于UDP),而非TCP。也说不准以后有新的实现。
补充说明:
协议换个角度理解那么就是规范,是大家一起编写的约定,这个规范随着时间推进,为了性能、安全性、易用性等等会发展出很多个版本。
包括ARP、TCP、IP、TLS、SSL、HTTP协议都是这么个意思。
只不过TCP(RFC793)、IPv4这种底层协议变化较少(IPv6是IP层协议的升级),应用层、表示层经常调整相对频繁(HTTP、TLS、SSL)。
其实很正常, 地基是基本上不会变的。因为改变的代价太大了,如果底层协议现在都还在改动,那所有相关的终端都得跟着变。向下兼容也不现实。
HTTP/2.0的改动比较大,大家有兴趣百度下具体区别。
浏览器访问的域名是 www.baidu.com,路径是 / ,百度服务器监听443端口的进程会根据请求的上下文 / ,返回相应的资源。
这里我们假设Web服务器使用的Nginx, 使用HTTP/1.1。
我们nginx.conf里配置 / 返回 /www/home/index.html 这个文件的内容。
补充说明:
TLS约定的通信端口是443,我们在浏览器里输入 https://www.baidu.com:443 和 https://www.baidu.com
是一样的效果,只是浏览器隐藏了443端口的显示。若改为非443端口,就需要我们手动在域名后加上这个端口。
80端口也一样。
http/1.1协议格式大致如下
GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0
Accept: text/html
Accept-Language: en-US
Connection: close
下面是百度首页的wireshark抓包。
补充说明:
如何用wireshark抓取解析加密流量大家可以上网查下。
image.png
编号244的报文就是请求百度首页资源的http请求(路径是 / ),以下是HTTP协议请求头内容
image.png
编号335的报文就是百度响应的首页内容,以下抓包内容,红色区域就是百度首页的HTML源码
image.png
至此,我们访问百度服务器,HTTP协议请求路径 / ,拿到了百度首页的资源。
剩下的就是浏览器根据HTML代码渲染页面。
4.TCP会话结束
上一步当浏览器解析HTML代码后,就能展示给用户了。
那么接下来呢?
此次HTTP请求完成。(对于HTTP/1.1,客户端发送request,服务端响应response即算作服务器应答完成。当然,大部分页面都会有很多个请求,我这里只用一次来计算)
理论上浏览器得到响应数据后,HTTP即完成了整个流程。接下来就是关闭TCP会话。
由于我们使用的是HTTP/1.1,如果HTTP协议请求里Connection的值设置为keep-alive(默认),TCP会话并不会立即断开。
如果是HTTP/1.0,或者kee-alive值为close,HTTP请求完成后会话都会断开。
断开TCP连接需要四次挥手。可以用wireshark抓包,关注FIN标志位来查看。
(理论上TCP协议挥手是四次:也就是两个FIN报文,两个ACK报文,因为TCP全双工。但是在浏览器中很少看到的四次,浏览器可以随时关闭,所以经常看到RST报文。)
补充说明:
挥手需要等待2MSL(目的是彻底让旧的报文数据从网络中消失)。
谁主动断开TCP连接,谁等待2MSL再释放端口。(Linux默认time-wait时间60s)
为何需要等待2MSL呢?
我们假设现在Client主动关闭。(Server也可以)
首先已经到达最后一步关闭连接的状态,因此我们可以认为网络丢包其实并非很严重。
实际上此时怎么断开连接都无所谓了,因为双方数据已经发送完毕。否则Client 压根不会收到Server最后发出FIN包,根本到达不了这一步。
那么假设最后一个ACK刚好在1MSL周期内达到Server,此时(特别巧合的情况下)Server刚好重传了一个FIN。这个FIN在1MSL时间内也必然会消失。
因此Client等待2MSL可以保证最后一个发送的ACK报文,Server可能发送的FIN报文都在网络中彻底消失。如果是Client
网友评论