当我们在浏览器的地址栏输入 www.cnblogs.com ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?这是前端面试经常碰到的问题,以下是我针对这个问题的一些总结。
一次完整的HTTP请求过程(以Chrome浏览器为例)
域名解析-> 发起TCP的3次握手 -> 建立TCP连接后发起http请求 -> 服务器响应http请求,浏览器得到html代码 -> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户
一、首先域名解析
1)Chrome浏览器 会首先搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的缓存中是否有www.cnblogs.com 对应的条目,而且没有过期,如果有且没有过期则解析到此结束。
2)如果浏览器自身的缓存里面没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束。
3)如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件(位于C:\Windows\System32\drivers\etc),看看这里面有没有该域名对应的IP地址,如果有则解析成功。
4) 如果在hosts文件中也没有找到对应的条目,浏览器请求解析IP地址(这个过程跟运营商有关,详情百度)。
二、发起TCP的3次握手
拿到域名对应的IP地址之后,User-Agent(一般是指浏览器)会以一个随机端口(1024 < 端口 < 65535)向服务器的WEB程序(常用的有httpd,nginx等)80端口发起TCP的连接请求,连接过程主要就是传说中的三次握手。
1)第一次握手:客户端发送了一个带有SYN(建立连接)的Tcp报文到服务器,这个三次握手中的开始。表示客户端想要和服务端建立连接。
2)第二次握手:服务端接收到客户端的请求,返回客户端报文,这个报文带有SYN(建立连接)和ACK(确认)标志,询问客户端是否准备好。
3)第三次握手:.客户端再次响应服务端一个ACK(确认),表示我已经准备好。
思考:为什么要三次握手呢,有人说两次握手就好了
举一个例子:已失效的连接请求报文段,
client发送了第一个连接的请求报文,但是由于网络不好,这个请求没有立即到达服务端,而是在某个网络节点中滞留了,直到某个时间才到达server,本来这已经是一个失效的报文,但是server端接收到这个请求报文后,还是会想client发出确认的报文,表示同意连接。假如不采用三次握手,那么只要server发出确认,新的建立就连接了,但其实这个请求是失效的请求,client是不会理睬server的确认信息,也不会向服务端发送确认的请求,但是server认为新的连接已经建立起来了,并一直等待client发来数据,这样,server的很多资源就没白白浪费掉了,采用三次握手就是为了防止这种情况的发生,server会因为收不到确认的报文,就知道client并没有建立连接。这就是三次握手的作用。
三、建立TCP连接后发起http请求
请求报文格式:起始行、请求头,请求主体。
1)起始行。包含请求方式,请求路径url,HTTP版本号。
请求方法:
- GET:从服务器获取一份文档
- HEAD :只从服务器获取文档的首部
- POST:向服务器发送需要处理的数据
- PUT:将请求的主体部分存储在服务器上
- TRACE:对可能经过代理服务器传送到服务器上去的报文进行跟踪
- OPTIONS:决定可以在服务器上执行哪些方法
- DELETE:从服务器上删除一份文档
请求的协议有哪些种?
- http/0.9: stateless
- http/1.0: MIME, keep-alive (保持连接), 缓存
- http/1.1: 更多的请求方法,更精细的缓存控制,持久连接(persistent connection) 比较常用
2)请求头。
包含若干个属性,格式为“属性名:属性值”,服务端据此获取客户端的信息。 列举一些
-
Accept 就是告诉服务器端,我接受那些MIME类型
-
Accept-Encoding 这个看起来是接受那些压缩方式的文件
-
Accept-Lanague 告诉服务器能够发送哪些语言
-
Connection 告诉服务器支持keep-alive特性
-
Cookie 每次请求时都会携带上Cookie以方便服务器端识别是否是同一个客户端
-
Host 用来标识请求服务器上的那个虚拟主机,比如Nginx里面可以定义很多个虚拟主机
-
User-Agent 用户代理,一般情况是浏览器,也有其他类型,如:wget curl 搜索引擎的蜘蛛等
-
条件请求首部: If-Modified-Since 是浏览器向服务器端询问某个资源文件如果自从什么时间修改过,那么重新发给我,这样就保证服务器端资源文件更新时,浏览器再次去请求,而不是使用缓存中的文件
-
安全请求首部: Authorization: 客户端提供给服务器的认证信息;
3)请求体。
它将一个页面表单中的组件值通过param1=value1¶m2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1¶m2=value2”的方式传递请求参数。
四、服务器端响应http请求,浏览器得到html代码。
响应报文格式:响应行、响应头,响应体。
1)响应行
响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK
常见状态码:
-
1xx: 信息性状态码
- 100,
- 101
-
2xx: 成功状态码
- 200:OK
-
3xx: 重定向状态码
- 301: 永久重定向, Location响应首部的值仍为当前URL,因此为隐藏重定向;
- 302: 临时重定向,显式重定向, Location响应首部的值为新的URL
- 304:Not Modified 未修改,比如本地缓存的资源文件和服务器上比较时,发现并没有修改,服务器返回一个304状态码,告诉浏览器,你不用请求该资源,直接使用本地的资源即可。
-
4xx: 客户端错误状态码
- 404: Not Found 请求的URL资源并不存在
-
5xx: 服务器端错误状态码
- 500: Internal Server Error 服务器内部错误
- 502: Bad Gateway 前面代理服务器联系不到后端的服务器时出现
- 504:Gateway Timeout 这个是代理能联系到后端的服务器,但是后端的服务器在规定的时间内没有给代理服务器响应
2)响应头
响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。
常见的响应头字段含义:
- Content-Encoding:文档的编码(Encode)方法。
- Allow:服务器支持哪些请求方法(如GET、POST等)。
- Content-Length:表示内容长度。
- Content- Type:表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置 Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
- Date:当前的GMT时间
- Expires:告诉浏览器把回送的资源缓存多长时间,-1或0则是不缓存。
- Last-Modified:文档的最后改动时间。
- Location:这个头配合302状态码使用,用于重定向接收者到一个新URI地址。
- Refresh:告诉浏览器隔多久刷新一次,以秒计。
- Server:服务器通过这个头告诉浏览器服务器的类型。
- Set-Cookie:设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie。
- addCookie:设置一个Cookie(Servlet API中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。
3)响应体
响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码。
五、浏览器解析html代码,并请求html代码中的资源
浏览器拿到index.html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序,但是由于每个资源大小不一样,而浏览器又多线程请求请求资源,所以从下图看出,这里显示的顺序并不一定是代码里面的顺序。
六、浏览器对页面进行渲染呈现给用户
最后,浏览器利用自己内部的工作机制,把请求到的静态资源和html代码进行渲染,渲染之后呈现给用户。