03-HTTP请求

86 阅读9分钟

Http请求

TCP连接过程包括了建立连接、传输数据、断开连接三个阶段

HTTP协议是建立在TCP连接基础之上的

HTTP是一种允许浏览器向服务器获取资源的协议,通常由浏览器发起请求,获取不同类型的文件

这里提出两个问题:

  • 为什么通常在第一次访问一个站点时,打开速度更慢,下一次访问时,速度就变快了
  • 当登录过一个网站之后,下次再次访问,就已经处于登录状态了,这是怎么做到的

浏览器端发起HTTP请求流程

假如在浏览器中输入地址http://baidu.com,那么接下来浏览器将会完成哪些动作?

构建请求

浏览器会构建请求行信息,构建好后,准备发起网络请求

 Get /index.html HTTP1.1

查找缓存

在真正发起网络请求之前,浏览器会先在浏览器缓存中查询是否有要请求的文件

其中,浏览器缓存是一种在本地保存资源副本,以供下次请求时直接使用的技术

如果浏览器发现请求的资源已经在浏览器缓存中有副本,那么就会拦截请求返回该副本,并直接结束请求,不会再去源服务器重新下载,好处:

  • 缓解服务器压力,提升性能
  • 对于网站来说,缓存是实现快速资源加载的重要组成部分

如果查找失败,就会进入网络请求过程

准备IP地址和端口

首先,先了解一下HTTP和TCP的关系

浏览器使用HTTP协议作为应用层协议,用来封装请求的文本信息,并使用TCP/IP做传输层协议将它发到网络上

所以在HTTP工作开始之前,浏览器需要通过TCP与服务器建立连接

HTTP于TCP建立连接.png 所以现在可以回答以下问题:

  • HTTP网络请求的第一步是做什么?

    是和服务器建立TCP连接

  • 建立TCP连接的第一步是准备IP地址和端口号,那么这两个准备好了吗?

    由于数据包都是通过IP地址传输给接收方,又是IP地址是数字表示,所以难以记忆,所以基于这个特点就出现了一个服务,负责把域名和IP地址做一一映射的关系,这套域名映射为IP的系统就叫做域名系统,简称DNS

    所以现在,浏览器会请求DNS返回域名对应的IP,除此之外,浏览器还提供了DNS数据缓存服务,如果某个域名已经被解析过了,那么浏览器就会缓存解析的结果,一共下次查询时直接使用,减少一次网络请求

    拿到IP后,接下来就要获取端口号了,通常情况下,如果URL没有特别指明端口号,那么HTTP协议默认是80端口

等待TCP队列

现在已经把端口和IP地址都准备好了,接下来理论上应该建立TCP连接了,但是事实并不是这样的

Chrome有个机制,同一个域名同时最多只能建立6个TCP连接,如果在同一个域名下同时有10个请求发生,那么其中4个请求会进入排队等待状态,直至进行中的请求完成

当然,如果当前请求数量少于6,会直接进入下一步,建立TCP连接

建立TCP连接

排队等待结束后,就可以通过TCP与服务器建立连接了

发送HTTP请求

一旦建立了TCP连接,浏览器就可以和服务器进行通信了

那么,浏览器是如何发送请求信息给服务器的?

首先浏览器会向服务器发送请求行,它包括了请求方法、请求URI和HTTP版本协议,发送请求行,就是告诉服务器浏览器需要什么资源

在浏览器发送请求行命令之后,还要以请求头形式发送其他一些信息,把浏览器的一些基础信息告诉服务器,比如包含了浏览器所使用的操作系统、浏览器内核等信息,以及当前请求的域名信息、浏览器端的Cookie信息,等等

浏览器发送请求信息.png

服务器端处理HTTP请求流程

经过上述过程,HTTP请求信息已经被送达服务器,接下来我们来了解一下服务器如何对于浏览器的请求信息做处理

返回请求

服务器处理结束之后,就可以返回数据给浏览器

这里可以使用工具软件**curl来查看返回请求数据**(i是为了返回响应行等数据):

 curl -i http://baidu.com

服务器响应请求信息.png

但不是所有的请求都可以被服务器处理,如果不能处理,服务器会通过响应行的状态码来告诉浏览器处理结果,如404

然后,服务器也会随同响应向浏览器发送响应头,响应头包含了服务器自身的一些信息,比如服务器生成返回数据的时间、返回的数据类型(JSON、HTML、流媒体等类型),以及服务器要在客户端保存的Cookie等信息

发送完响应头后,服务器就可以继续发送响应体的数据,通常,响应体就包含了HTML的实际内容

断开连接

如果服务器向客户端返回了请求数据,他就要关闭TCP连接

如果浏览器或服务器在其头信息中加入了**Connection: Keep-Alive,那么TCP连接在发送之后就会保持打开状态**,这样浏览器就可以继续通过同一个TCP发送请求

这样做的好处是可以省去下次请求时需要建立连接的时间,提升资源加载速度

重定向

这里有一种特殊情况,比如现在在浏览器中打开https://geekbang.org/后,你会发现最终打开的页面地址是 https://www.geekbang.org

这两个URL之所以不一样,是因为涉及到了一个重定向操作

这里我们可以通过命令行查看一下请求https://geekbang.org/ 会返回什么内容?

服务器重定向.png 可以看到,响应行返回的状态码是301,状态301就是告诉浏览器,我需要重定向到另外一个网址,而需要重定向的网址正是包含在响应头的Location字段中

接下来,浏览器获取Location字段中的地址,并使用该地址重新导航,这就是一个完整重定向的执行流程

问题解答

问题一

为什么很多站点第二次打开速度会很快?

如果第二次页面打开很快,主要原因是第一次加载页面过程中,缓存了一些耗时的数据

而可以缓存的数据有DNS和页面资源,这里着重分析浏览器的资源缓存:

浏览器资源缓存.png

  • 第一次请求:

    服务器返回HTTP响应头给浏览器,浏览器通过响应头的Cache-control字段来设置是否缓存该资源

    通常,我们还需要为这个资源设置一个缓存过期时长,而这个时长是通过**Cache-Control中的Max-age参数**来设置的,比如上图设置的缓存过期时间是2000秒

  • 第二次请求:

    在该缓存资源还未过期的情况下, 如果再次请求该资源,会直接返回缓存中的资源给浏览器

  • 第三次请求:

    由于缓存过期了,所以浏览器则会继续发起网络请求,并且在HTTP请求头中带上:

     If-None-Match:"4f80f-13c-3a1xb12a"
    

    服务器收到请求头后,会根据If-None-Match的值来判断请求的资源是否有更新:

    • 如果没有更新,就返回304状态码,相当于服务器告诉浏览器:这个缓存可以继续使用,这次就不重复发送数据给你了
    • 如果资源有更新,服务器就直接返回最新资源给浏览器

总的来说:很多网站第二次访问能够秒开,是因为这些网站把很多资源都缓存在了本地,浏览器缓存直接使用本地副本来回应请求,而不会产生真实的网络请求,从而节省了时间,同时,DNS数据也被浏览器缓存了,这又省去了DNS查询环节

问题二

登录状态是如何保持的?

用户打开登录页面,在登录框里填入用户名和密码,点击确定按钮。点击按钮会触发页面脚本生成用户登录信息,然后调用POST方法提交用户登录信息给服务器

服务器接收到浏览器提交的信息之后,查询后台,验证用户登录信息是否正确,如果正确的话,会生成一段表示用户身份的字符串,并把该字符串写到响应头的Set-Cookie字段里,如下所示,然后把响应头发送给浏览器

 Set-Cookie: UID=3431uad;

浏览器在接收到服务器的响应头后,开始解析响应头,如果遇到响应头里含有Set-Cookie字段的情况,浏览器就会把这个字段信息保存到本地

当用户再次访问时,浏览器会发起HTTP请求,但在发起请求之前,浏览器会读取之前保存的Cookie数据,并把数据写进请求头里的Cookie字段里(如下所示),然后浏览器再将请求头发送给服务器

 Cookie: UID=3431uad;

服务器在收到HTTP请求头数据之后,就会查找请求头里面的“Cookie”字段信息,当查找到包含UID=3431uad的信息时,服务器查询后台,并判断该用户是已登录状态,然后生成含有该用户信息的页面数据,并把生成的数据发送给浏览器

浏览器在接收到该含有当前用户的页面数据后,就可以正确展示用户登录的状态信息了

总的来说:如果服务器端发送的响应头内有 Set-Cookie 的字段,那么浏览器就会将该字段的内容保持到本地。当下次客户端再往该服务器发送请求时,客户端会自动在请求头中加入 Cookie 值后再发送出去。服务器端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到该用户的状态信息