浏览器打开一个网页时都发生了什么

转自:https://huozhi.github.io/2015/03/15/network/2014-06-17-What-Happened-After-Search-URL-in-Browser/


浏览器解析URL

选择协议并找出你请求的资源,你可能打开的是一个基于http协议的网站主页
Protocol “http:” 使用HTTP协议
Resource “/“ 请求的资源是根目录,一般是主页

如果我地址栏里的不是url链接怎么办?(是的话跳过这步)

当协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎。大部分情况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎这次搜索来自这个特定浏览器

检查HSTS列表(Https)

浏览器检查自带的“预加载HSTS(HTTP严格传输安全)”列表,这个列表里包含了那些请求浏览器只使用HTTPS进行连接的网站

浏览器向网站发出第一个HTTP请求之后,网站会返回浏览器一个响应,请求浏览器只使用HTTPS发送请求。然而,就是这第一个HTTP请求,却可能会使用户收到 downgrade attack 的威胁,这也是为什么现代浏览器都预置了HSTS列表。

如果你没有证书(certificate),肯定会gg的

转换非ASCII的Unicode字符

浏览器检查输入是否含有不是 a-z, A-Z,0-9, - 或者 . 的字符
这里主机名是 google.com,所以没有非ASCII的字符,如果有的话,浏览器会对主机名部分使用 Punycode 编码

DNS查询

找hosts文件
为了科学上网,很多人的hosts文件都长得不行,这样就一定程度上避免了某墙的DNS污染
找本地DNS解析器缓存
如果缓存中没有,就去调用 gethostbynme 库函数进行查询
如果hosts没有这个域名的记录,也没有在本地DNS解析器缓存里找到,就去DNS服务器找。DNS服务器是由网络通信栈提供的,通常是本地路由器或者ISP的缓存DNS服务器

查询本地 DNS 服务器会按照ARP协议(address resolution protocol)寻找,在另一篇博客里讲过怎么查

现在我们有了DNS服务器或者默认网关的IP地址,我们可以继续DNS请求了:

使用53端口向DNS服务器发送UDP请求包,如果响应包太大,会使用TCP
如果本地/ISP DNS服务器没有找到结果,它会发送一个递归查询请求,一层一层向高层DNS服务器做查询,直到查询到起始授权机构,如果找到会把结果返回

使用套接字

当浏览器得到了目标服务器的IP地址,以及URL中给出来端口号(http协议默认端口号是80, https默认端口号是443),它会调用系统库函数 socket ,请求一个 TCP流套接字,对应的参数是 AF_INET 和SOCK_STREAM 。

这个请求首先被交给传输层,在传输层请求被封装成TCP segment。目标端口会会被加入头部,源端口会在系统内核的动态端口范围内选取(Linux下是ip_local_port_range)
TCP segment被送往网络层,网络层会在其中再加入一个IP头部,里面包含了目标服务器的IP地址以及本机的IP地址,把它封装成一个TCP packet。
这个TCP packet接下来会进入链路层,链路层会在封包中加入frame头部,里面包含了本地内置网卡的MAC地址以及网关(本地路由器)的MAC地址。像前面说的一样,如果内核不知道网关的MAC地址,它必须进行ARP广播来查询其地址。

到了现在,TCP封包已经准备好了,可是使用下面的方式进行传输:

以太网
WiFi
蜂窝数据网络

对于大部分家庭网络和小型企业网络来说,封包会从本地计算机出发,经过本地网络,再通过调制解调器把数字信号转换成模拟信号,使其适于在电话线路,有线电视光缆和无线电话线路上传输。在传输线路的另一端,是另外一个调制解调器,它把模拟信号转换回数字信号,交由下一个 网络节点 处理。节点的目标地址和源地址将在后面讨论。

大型企业和比较新的住宅通常使用光纤或直接以太网连接,这种情况下信号一直是数字的,会被直接传到下一个 网络节点 进行处理。

最终封包会到达管理本地子网的路由器。在那里出发,它会继续经过自治区域的边界路由器,其他自治区域,最终到达目标服务器。一路上经过的这些路由器会从IP数据报头部里提取出目标地址,并将封包正确地路由到下一个目的地。IP数据报头部TTL域的值每经过一个路由器就减1,如果封包的TTL变为0,或者路由器由于网络拥堵等原因封包队列满了,那么这个包会被路由器丢弃。

上面的发送和接受过程在TCP连接期间会发生很多次:

客户端选择一个初始序列号(ISN),将设置了SYN位的封包发送给服务器端,表明自己要建立连接并设置了初始序列号
服务器端接受到SYN包,如果它可以建立连接:
服务器端选择它自己的初始序列号
服务器端设置SYN位,表明自己选择了一个初始序列号
服务器端把 (客户端ISN + 1) 复制到ACK域,并且设置ACK位,表明自己接收到了客户端的第一个封包
客户端通过发送下面一个封包来确认这次连接:
自己的序列号+1
接收端ACK+1
设置ACK位
数据通过下面的方式传输:
当一方发送了N个Bytes的数据之后,将自己的SEQ序列号也增加N
另一方确认接收到这个数据包(或者一系列数据包)之后,它发送一个ACK包,ACK的值设置为接收到的数据包的最后一个序列号
关闭连接时:
要关闭连接的一方发送一个FIN包
另一方确认这个FIN包,并且发送自己的FIN包
要关闭的一方使用ACK包来确认接收到了FIN

UDP 数据包

TLS 握手

客户端发送一个 Client hello 消息到服务器端,消息中同时包含了它的TLS版本,可用的加密算法和压缩算法。
服务器端向客户端返回一个 Server hello 消息,消息中包含了服务器端的TLS版本,服务器选择了哪个加密和压缩算法,以及服务器的公开证书,证书中包含了公钥。客户端会使用这个公钥加密接下来的握手过程,直到协商生成一个新的对称密钥
客户端根据自己的信任CA列表,验证服务器端的证书是否有效。如果有效,客户端会生成一串伪随机数,使用服务器的公钥加密它。这串随机数会被用于生成新的对称密钥
服务器端使用自己的私钥解密上面提到的随机数,然后使用这串随机数生成自己的对称主密钥
客户端发送一个 Finished 消息给服务器端,使用对称密钥加密这次通讯的一个散列值
服务器端生成自己的 hash 值,然后解密客户端发送来的信息,检查这两个值是否对应。如果对应,就向客户端发送一个 Finished 消息,也使用协商好的对称密钥加密
从现在开始,接下来整个 TLS 会话都使用对称秘钥进行加密,传输应用层(HTTP)内容

TCP 数据包

HTTP 协议

如果浏览器是Google出品的,它不会使用HTTP协议来获取页面信息,而是会与服务器端发送请求,商讨使用SPDY协议。

如果浏览器使用HTTP协议,它会向服务器发送这样的一个请求:

GET / HTTP/1.1
Host: google.com
[其他头部]

“其他头部”包含了一系列的由冒号分割开的键值对,它们的格式符合HTTP协议标准,它们之间由一个换行符分割开来。这里我们假设浏览器没有违反HTTP协议标准的bug,同时浏览器使用 HTTP/1.1 协议,不然的话头部可能不包含 Host 字段,同时 GET 请求中的版本号会变成 HTTP/1.0 或者 HTTP/0.9 。

HTTP/1.1 定义了“关闭连接”的选项 “close”,发送者使用这个选项指示这次连接在响应结束之后会断开:

Connection:close

不支持持久连接的 HTTP/1.1 必须在每条消息中都包含 “close” 选项。

在发送完这些请求和头部之后,浏览器发送一个换行符,表示要发送的内容已经结束了。

服务器端返回一个响应码,指示这次请求的状态,响应的形式是这样的:

200 OK
[response headers]

然后是一个换行,接下来有效载荷(payload),也就是 www.google.com 的HTML内容。服务器下面可能会关闭连接,如果客户端请求保持连接的话,服务器端会保持连接打开,以供以后的请求重用。

如果浏览器发送的HTTP头部包含了足够多的信息(例如包含了 Etag 头部,以至于服务器可以判断出,浏览器缓存的文件版本自从上次获取之后没有再更改过,服务器可能会返回这样的响应:

304 Not Modified
[response headers]

这个响应没有有效载荷,浏览器会从自己的缓存中取出想要的内容。

在解析完HTML之后,浏览器和客户端会重复上面的过程,直到HTML页面引入的所有资源(图片,CSS,favicon.ico等等)全部都获取完毕,区别只是头部的 GET / HTTP/1.1 会变成 GET /$(相对www.google.com的URL) HTTP/1.1 。

如果HTML引入了 www.google.com 域名之外的资源,浏览器会回到上面解析域名那一步,按照下面的步骤往下一步一步执行,请求中的 Host 头部会变成另外的域名。

HTTP服务器请求处理

HTTPD(HTTP Daemon)在服务器端处理请求/相应。最常见的 HTTPD 有 Linux 上常用的 Apache 和 nginx,与 Windows 上的 IIS。

HTTPD接收请求

服务器把请求拆分为以下几个参数:

HTTP请求方法(GET, POST, HEAD, PUT 和 DELETE )。在访问Google这种情况下,使用的是GET方法
域名:google.com
请求路径/页面:/ (我们没有请求google.com下的指定的页面,因此 / 是默认的路径)

服务器验证其上已经配置了google.com的虚拟主机

服务器验证google.com接受GET方法

服务器验证该用户可以使用GET方法(根据IP地址,身份信息等)

如果服务器安装了 URL 重写模块(例如 Apache 的 mod_rewrite 和 IIS 的 URL Rewrite),服务器会尝试匹配重写规则,如果匹配上的话,服务器会按照规则重写这个请求

服务器根据请求信息获取相应的响应内容,这种情况下由于访问路径是 “/” ,会访问首页文件。(你可以重写这个规则,但是这个是最常用的)

服务器会使用指定的处理程序分析处理这个文件,比如假设Google使用PHP,服务器会使用PHP解析index文件,并捕获输出,把PHP的输出结果给请求者

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值