http过程详解

2,091 阅读8分钟

当我们在浏览器的地址栏输入 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&param2=value2的键值对形式编码成一个格式化串,它承载多个请求参数的数据。不但报文体可以传递请求参数,请求URL也可以通过类似于“/chapter15/user.html? param1=value1&param2=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代码进行渲染,渲染之后呈现给用户。