说明
浏览器工作原理与实践专栏学习笔记
HTTP 是一种允许浏览器向服务器获取资源的协议,是 Web 的基础,HTTP 也是浏览器使用最广的协议,所以要想学好浏览器,就要先深入了解 HTTP。
浏览器端发起 HTTP 请求流程
如果你在浏览器地址栏里键入极客时间网站的地址:http://time.geekbang.org/index.html
, 浏览器会完成哪些动作呢?
1. 构建请求
首先,浏览器构建请求行信息(如下所示),构建好后,浏览器准备发起网络请求。
GET /index.html HTTP1.1
2. 查找缓存
在真正发起网络请求之前,浏览器会先在浏览器缓存中查询是否有要请求的文件。
浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术。
3. 准备 IP 地址和端口
1、浏览器使用 HTTP 协议作为应用层协议
,用来封装请求的文本信息;
2、并使用 TCP/IP 作传输层协议
将它发到网络上。
所以在 HTTP 工作开始之前,浏览器需要通过 TCP 与服务器建立连接。也就是说 HTTP 的内容是通过 TCP 的传输数据阶段来实现的
。
如图:TCP 和 HTTP 的关系示意图
浏览器会请求 DNS 返回域名对应的 IP。
浏览器还提供了 DNS 数据缓存服务。
4. 等待 TCP 队列
Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。
如果当前请求数量少于 6,会直接进入下一步,建立 TCP 连接。
引用:“Geek_870e7d
:http/1.1 一个tcp同时只能处理一个请求,浏览器会为每个域名维护6个tcp连接! 但是每个tcp连接是可以复用的,也就是处理完一个请求之后,不断开这个tcp连接,可以用来处理下个http请求! 不过http2是可以并行请求资源的,所以如果使用http2,浏览器只会为每个域名维护一个tcp连接”
5. 建立 TCP 连接
有兴趣的:这个部分可以看第二讲
6. 发送 HTTP 请求
首先浏览器会向服务器发送请求行,它包括了请求方法
、请求 URI(Uniform Resource Identifier)
和 HTTP 版本协议
。
HTTP 请求数据格式:
服务器端处理 HTTP 请求流程
1. 返回请求
首先服务器会返回响应行
,包括协议版本和状态码。
服务器响应的数据格式:
可以通过工具软件 curl
来查看返回请求数据:(【curl网站开发指南-阮一峰的网络日志
】)
curl -i https://time.geekbang.org/
-i
是为了返回响应行、响应头和响应体的数据。
2. 断开连接
通常情况下,一旦服务器向客户端返回了请求数据,它就要关闭 TCP 连接。
如果浏览器或者服务器在其头信息中加入了:
Connection:Keep-Alive
那么 TCP 连接在发送后将仍然保持打开状态。
保持 TCP 连接可以省去下次请求时需要建立连接的时间,提升资源加载速度。
3. 重定向
当你在浏览器中打开 geekbang.org
后,你会发现最终打开的页面地址是 https://www.geekbang.org
。
这两个 URL 之所以不一样,是因为涉及到了一个重定向操作
。
在控制台输入如下命令:
curl -I geekbang.org
-I
表示只需要获取响应头和响应行数据。
服务器返回响应行和响应头(含重定向格式):
- 响应行返回的状态码是 301
- 状态 301 就是告诉浏览器,需要重定向到另外一个网址
- 需要重定向的网址正是包含在响应头的 Location 字段中
问题解答
1. 为什么很多站点第二次打开速度会很快?
解答:主要原因是这些网站把很多资源都缓存在了本地,浏览器缓存直接使用本地副本来回应请求,而不会产生真实的网络请求,从而节省了时间。同时,DNS 数据也被浏览器缓存了,这又省去了 DNS 查询环节。
DNS 缓存和页面资源缓存这两块数据是会被浏览器缓存的。
1)、DNS 缓存比较简单,它主要就是在浏览器本地把对应的 IP 和域名关联起来。
2)、重点看下浏览器资源缓存,下面是缓存处理的过程:
浏览器是通过响应头中的 Cache-Control
字段来设置是否缓存该资源。
Cache-Control:Max-age=2000
上面表示:缓存过期时间是 2000 秒。
如果缓存过期了,浏览器则会继续发起网络请求,并且在 HTTP 请求头中带上:
If-None-Match: "4f80f-13c-3a1xb12a"
服务器收到请求头后,会根据 If-None-Match
的值来判断请求的资源是否有更新。
- 如果没有更新,就返回 304 状态码,相当于服务器告诉浏览器:“这个缓存可以继续使用,这次就不重复发送数据给你了。”
- 如果资源有更新,服务器就直接返回最新资源给浏览器。
更多的HTTP 缓存参考链接:HTTP 缓存-MDN
2. 登录状态是如何保持的?
解答:如果服务器端发送的响应头内有 Set-Cookie
的字段,那么浏览器就会将该字段的内容保持到本地。当下次客户端再往该服务器发送请求时,客户端会自动在请求头中加入 Cookie 值后再发送出去。服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到该用户的状态信息。
1)、当用户登录信息正确,会生成一段表示用户身份的字符串,并把该字符串写到响应头的 Set-Cookie 字段里,然后把响应头发送给浏览器。
比如下面所示:
Set-Cookie: UID=3431uad;
2)、浏览器在接收到服务器的响应头后,开始解析响应头,如果遇到响应头里含有 Set-Cookie
字段的情况,浏览器就会把这个字段信息保存到本地。
比如把上面的 UID=3431uad
保存到本地。
3)、当用户再次访问时,浏览器会发起 HTTP 请求,但在发起请求之前,浏览器会读取之前保存的 Cookie 数据,并把数据写进请求头里的 Cookie 字段里,然后浏览器再将请求头发送给服务器。
比如下面所示:
Cookie: UID=3431uad;
4)、服务器在收到 HTTP 请求头数据之后,就会查找请求头里面的 “Cookie” 字段信息,当查找到包含 UID=3431uad
的信息时,服务器查询后台,并判断该用户是已登录状态,然后生成含有该用户信息的页面数据,并把生成的数据发送给浏览器。
Cookie 流程图:
总结
HTTP 请求流程示意图:
精选留言
1、老师,同一个域名同时最多只能建立 6 个 TCP 连接 是不是意思是统一域名同时只能发送6个AJAX请求吗, TCP连接和AJAX请求有什么关系吗?
作者回复: 首先回答第一个问题:
”同一个域名同时最多只能建立 6 个 TCP 连接“ 指的不光是指Ajax,还包括页面中的资源加载,只要是一个域名下的资源,浏览器同一时刻最多只支持6个并行请求。
不过这是HTTP/1.1的规则,HTTP/2已经不用这套规则了,而且HTTP/2也很成熟了,有条件可以考虑切换到HTTP/2.
Ajax其实就是HTTP请求,包括了XMLHttpRequest和Fetch,HTTP请求是建立在TCP协议之上的。
2、老师,请问https为什么能防止网络劫持?
作者回复: http在传输过程中是明文的,所以数据在传输过程中是能够被截获或者修改的,比如谁在你电脑上安装了一个网络拦截软件,或者你的路由器被谁安装了监听软件,甚至在网络服务提供商都有可能修改你页面的内容,基于这些原因,我们需要在传输过程中加密数据,这就是https出现的原因,即便你拦截到了请求,获取的只是加密后的数据,拿到也没有什么用。
这块在浏览器安全篇会系统介绍。
3、老师您好~有个问题困扰我了。希望您能解答我,十分感谢。http2同个域名只能维持一个长连接。那我现在打开了一个域名下的a页面,然后又打开了这个域名的b页面,那这个b页面是新开一个tcp长连接吗?还是会用a页面的长连接?换句话说,维持一个tcp长连接,指的是一个页面维持一个?还是整个浏览器维持一个?还是同一个渲染进程维持一个?
作者回复: 浏览器为用同一个域名只维护一个 TCP 连接。
你从通过 Chrome 打开chrome://net-export/
这个地址,然后记录网络过程。
最后在使用这个https://netlog-viewer.appspot.com/
打开你日志文件,就能看到 h2 的详细信息了。
4、浏览器刷新操作,ctrl+F5和F5有什么区别?
作者回复: 一个是强制刷新,也就是资源都走网络。
一个是正常处理流程。
比如通过网络面板,打开一个站点,再使用强制刷新,可以看到如下信息
176 requests
3.1 MB transferred
3.5 MB resources
Finish: 26.30 s
DOMContentLoaded: 5.04 s
Load: 14.88 s
如果使用正常的刷新,看到的信息如下:
171 requests
419 KB transferred
3.2 MB resources
Finish: 25.09 s
DOMContentLoaded: 1.41 s
Load: 6.24 s
其中的 transferred 是真正的网络传输的数据,使用强制刷新,传输的数据体积就大多了,而且请求时间也变得更长了。
5、set cookie 会不会有安全问题,麻烦老师指导下
作者回复: cookie 是不安全的,比如黑客可以通过一些手段插入一些脚本到你的页面里面(具体一些途径我们浏览器安全篇再讲),通过脚本获取到你的 cookie 数据,然后就可以利用 cookie 做一些坏事了。
当然也有一些方法规避,常用的一个是将部分 cookie 设置成 httponly 的属性,设置了 httponly 属性后 cookie,就无法通过js脚本来读取了,只是在发送 http 请求时候会被带上!
当然还有一些其他防范的方法,这个同样咱们后面在聊!