# 什么是 HTTP 协议

HTTP 协议即超文本传送协议 (HyperText Transfer Protocol) ,使用经典的 C/S 模式交互,它规定了客户端与服务器之间进行网页内容传输时,所必须遵守的传输格式。

TCP/IP 网络模型中,它位于应用层采用传输层采用的是 TCP 协议,端口号默认是 80。虽然它采用的是 TCP 协议,但它实际上是一个无连接协议,这里的无连接是指服务器每次处理完客户端请求并收到客户端的响应回答后就会马上断开连接,并不会一直维持会话连接状态。同时它是一个无状态协议,即它并不会记录前后报文之间的关系。每一个请求报文在它看来都是独立的,没有上下文联系。这就给我们实际运用的时候带来了一些麻烦,例如在购物网站购买商品时,我们商品加入自己账号的购物车这就需要上下文联系。为了解决这个问题于是就有了 cookie-session机制 来帮助我们建立上下文联系。

# 为什么要 HTTP 协议

它是为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的,例如现在的电子邮箱流行使用 http 协议去传输内容。

# HTTP 报文格式

http 的报文可以大致分为请求报文和响应报文两种,请求报文即为客户端向服务器发送的请求消息,而响应报文则是服务器根据客户端的请求响应回去的消息。

# 请求报文

HTTP 请求消息由请求行(request line)、请求头部( header ) 、空行 和 请求体 4 个部分组成。

请求报文基本格式

# 常见的请求头部

头部字段说明
Host要请求的服务器域名
Connection客户端与服务器的连接方式 (close 或 keepalive)
Content-Length用来描述请求体的大小
Accept客户端可识别的响应内容类型列表
User-Agent产生请求的浏览器类型
Content-Type客户端告诉服务器实际发送的数据类型
Accept-Encoding客户端可接收的内容压缩编码形式
Accept-Language用户期望获得的自然语言的优先顺序

我们可以在浏览器里具体抓一个请求报头来看看

GET请求头

POST请求头

从上面请求头的对比中里我们可以得知一个结论。GET 请求和 POST 请求的一个重要区别就是 GET 请求方式中,我们要发送的数据会携带在请求报头中,实际的请求体是为空的。而在 POST 请求方式中,我们要提交的数据会携带在请求体中,并不会夹带在请求头中。

# 响应报头

HTTP 响应消息由状态行、响应头部、空行 和 响应体 4 个部分组成,如下图所示:

响应报文基本格式

实际浏览器中的响应报头

GET响应报头

POST响应报头

# 常见响应报头

头部字段说明
Connect-Encoding服务器返回数据的压缩编码格式
Content-Length用来描述响应体的大小
Content-Type服务器实际发送的数据类型
Set-Cookie服务端给客户端设置的 cookie
Date数据返回时间

# http 请求方法

序号方法描述
1GET(查询) 发送请求来获得服务器上的资源,请求体中不会包含请求数据,请求数据放在协议头中。
2POST(新增) 向服务器提交资源(例如提交表单或上传文件)。数据被包含在请求体中提交给服务器。
3PUT(修改) 向服务器提交资源,并使用提交的新资源,替换掉服务器对应的旧资源。
4DELETE(删除) 请求服务器删除指定的资源。
5HEADHEAD 方法请求一个与 GET 请求的响应相同的响应,但没有响应体。
6OPTIONS获取 http 服务器支持的 http 请求方法,允许客户端查看服务器的性能,比如 ajax 跨域时的预检等。
7CONNECT建立一个到由目标资源标识的服务器的隧道。
8TRACE沿着到目标资源的路径执行一个消息环回测试,主要用于测试或诊断。
9PATCH是对 PUT 方法的补充,用来对已知资源进行局部更新 。

# http 响应状态码

http 的状态码可以分为 5 大类型

分类分类描述
1**信息,服务器收到请求,需要请求者继续执行操作(实际开发中很少遇到 1** 类型的状态码)
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

# 服务器处理相关的响应状态码

状态码状态码英文名称中文描述
100Continue继续。客户端应继续其请求
101Switching Protocols切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 新版本

# 成功相关的响应状态码

**2xx 范围的状态码,表示服务器已成功接收到请求并进行处理。常见的 2 类型的状态码如下:

状态码状态码英文名称中文描述
200OK请求成功。一般用于 GET 与 POST 请求
201Created已创建。成功请求并创建了新的资源,通常用于 POST 或 PUT 请求
206Partial Content返回部分内容。服务器成功处理了部分 GET 请求

# 重定向相关的响应状态码

**3xx 范围的状态码,表示表示服务器要求客户端重定向,需要客户端进一步的操作以完成资源的请求。常见的 3 类型的状态码如下:

状态码状态码英文名称中文描述
301Moved Permanently永久重定向。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替
302Found临时重定向。与 301 类似。但资源只是临时被移动。客户端应继续使用原有 URI
304Not Modified未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源(响应消息中不包含响应体)。用于协商缓存

# 客户端错误相关的响应状态码

4** 范围的状态码,表示客户端的请求有非法内容,从而导致这次请求失败。常见的 4** 类型的状态码如下:

状态码状态码英文名称中文描述
400Bad Request语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。
401Unauthorized当前请求需要用户验证。
403Forbidden用户无权限,服务器拒绝执行它。
404Not Found服务器无法根据客户端的请求找到资源(网页)。
408Request Timeout请求超时。服务器等待客户端发送的请求时间过长,超时。

# 服务端错误相关的响应状态码

5** 范围的状态码,表示服务器未能正常处理客户端的请求而出现意外错误。前端甩锅码,只要出现 5 开头的响应码全部甩锅后端哈哈。常见的 5** 类型的状态码如下:

状态码状态码英文名称中文描述
500Internal Server Error服务器内部错误,无法完成请求。
501Not Implemented服务器不支持该请求方法,无法完成请求。只有 GET 和 HEAD 请求方法是要求每个服务器必须支持的,其它请求方法在不支持的服务器上会返回 501
503Service Unavailable由于超载或系统维护,服务器暂时的无法处理客户端的请求。
504Gateway Timeout网关超时

更多状态码信息

# http 缓存机制

第一次访问网站时服务端必须把所有资源都给浏览器,此时 http 会把一些资源缓存到本地浏览器这样下次访问该网站时就可以少传输一些数据能够有效地加快网站打开速度。一般来说静态资源如 jscss 、图谱等资源都可以被缓存,但 html 、业务数据等资源我们一般不会去缓存因为这些资源变化比较频繁我们需要时刻去获取最新的以保证正确性。

# http 缓存策略

# 强制缓存

强制缓存是指当我们在首次访问网页时,服务器返回给我们资源会在响应体里加上 Cache-Control 响应头来表示该资源可以被缓存,此时我们本地就会将该资源缓存下来。当我们第二次访问该网页时,浏览器就会根据 Cache-control 的记录尝试去本地缓存中寻找资源,若找到该资源则缓存命中不会再发送网络请求向服务器要资源。

查找缓存根据 Cache-control 的值来查找,当值为 max-age 时就会根据其数值来计算出该缓存是否过期,若资源过期则向服务器请求资源;当值为 no-cache 时说明我们不用本地缓存,浏览器直接向服务器请求资源,服务端如何处理浏览器不管;当值为 no-store 时表明我们既不用本地缓存也不理会服务器协商缓存的策略,直接要求服务器返回新资源;当值为 private 时表示我们只在终端用户缓存资源,不允许中间路由缓存资源;当值为 public 时我们允许中间路由缓存资源。

image-20220224213049496

# 协商缓存

协商缓存是服务器的一种缓存策略。当服务器接收到客户端请求资源的消息时,服务器可以做三种选择一是直接满足客户端的要求发送资源;二是比对客户端携带的资源标识根据标识来告诉客户端它的本地缓存有没有过期,若发现客户端的资源和服务器一致则告诉客户端可以直接使用本地缓存而服务器本身不会再次发送资源;三是服务器拒绝客户端的请求。

上面第二种情况就是典型的协商缓存。在协商缓存的过程中,客户端第一次访问资源服务器会在响应报文的响应头中加入 Last-Modified 字段来作为该资源的资源标识,该字段表示资源最后的修改时间。当客户端第二次请求资源时,在请求报文的请求头中加入 If-Modified-Since 字段来提供给服务器进行比对,该字段的值就是上次请求该资源时服务器设置的 Last-Modified 字段值。若服务端比对发现客户端的资源和服务器资源一致则返回 304 状态码告诉客户端它的本地缓存是可用的,若不一致则返回新的资源和 Last-Modified

image-20220224213010137

除了可以将 Last-Modified 作为资源标识,服务器还可以使用 Etag 作为资源的唯一标识。 Last-Modified 作为资源标识只能精确到秒级,而 Etag 可以更加精确一些因为它是根据文件内容来生成的唯一标识。若一个文件内容没有被修改,但是它一直被重复创建,这种情况下如果使用 Last-Modified 做资源标识就会导致服务器不断地发送资源给客户端即便资源本身并没有变化。如果使用 Etag 就可以避免这中情况的发生。 Last-ModifiedEtag 并不是互斥关系,它们可以同时使用,但它们同时出现时 Etag 的优先级会更高,服务器会一 Etag 的比较结果为准。

image-20220224213414054

# http 缓存相关的报头

头部字段说明
Cache-Control当该资源能够被缓存时,响应头里就会加上该字段以控制强制缓存的逻辑
Last-Modified资源最后修改时间,用作资源标识。在协商缓存中判断资源是否更新
Etag资源唯一标识。作用同 Last-Modified

# 刷新操作对缓存的影响

页面的刷新可以分为正常刷新(页面的前进后退、地址栏输入 URL、链接跳转等)、手动刷新(F5、点击刷新按钮、右键菜单刷新等)、强制刷新(Ctrl + F5 等)。当我们正常刷新时强制缓存和协商缓存都会正常生效,但当我们使用手动刷新时强制缓存会失效,仅协商缓存会生效。若我们使用强制刷新则强制缓存和协商缓存都会失效。

更多 http 缓存信息

# http 协议演进历史

# http 0.9

  • HTTP 0.9 是最早发布出来的一个版本,于 1991 年发布。

  • 它只接受 GET 一种请求方法,没有在通讯中指定版本号,且不支持请求头。由于该版本不支持 POST 方法,因此客户端无法向服务器传递太多信息。

  • HTTP 0.9 具有典型的无状态性,每个事务独立进行处理,事务结束时就释放这个连接。HTTP 协议的无状态特点在其第一个版本中已经成型。

# http 1.0

HTTP 1.0 是 HTTP 协议的第二个版本,于 1996 年发布,如今仍然被广泛使用,尤其是在代理服务器中。

这是第一个在通讯中指定版本号的 HTTP 协议版本,具有以下特点:

  • 不仅仅支持 GET 命令,还支持 POST 和 HEAD 等请求方法。

  • HTTP 的请求和回应格式也发生了变化,除了要传输的数据之外,每次通信都包含头信息,用来描述一些信息。

  • 不再局限于 0.9 版本的纯文本格式

    根据头信息中的 Content-Type 属性,可以支持多种数据格式,这使得互联网不仅仅可以用来传输文字,还可以传输图像、音频、视频等二进制文件。

  • 开始支持 cache,就是当客户端在规定时间内访问同一网站,直接访问 cache 即可。

  • 其他的新增功能还包括状态码(status code)、多字符集支持、多部分发送(multi-part type)、权限(authorization)、缓存(cache)、内容编码(content encoding)等。

1.0 版本的工作方式是每次 TCP 连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立 TCP 连接。 TCP 连接的建立成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。

HTTP 1.0 版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。为了解决这个问题,有些浏览器在请求时,即在请求头部加上 Connection 字段:

image-20220302164936722

这个字段要求服务器不要关闭 TCP 连接,以便其他请求复用。服务器同样回应这个字段。

一个可以复用的 TCP 连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是一个标准字段,不同实现的行为可能不一致,因此不是根本的解决办法。

# http 1.1

  • 默认采用持续连接(Connection: keep-alive)

  • 还支持以管道方式在同时发送多个请求,以便降低线路负载,提高传输速度。但是服务器响应请求并不能并发,只能串行发出,等上一个请求响应结束以后才能发出第二个请求的响应

  • 分块传输编码。使用 Content-Length 字段的前提条件是,服务器发送回应之前,必须知道回应的数据长度。对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用 "流模式"(stream)取代 "缓存模式"(buffer)。

因此,HTTP 1.1 版本规定可以不使用 Content-Length 字段,而使用 "分块传输编码"(chunked transfer encoding)。只要请求或回应的头信息有 Transfer-Encoding 字段,就表明回应将由数量未定的数据块组成。

  • 新增了请求方式 PUT、PATCH、OPTIONS、DELETE 等。

  • 客户端请求的头信息新增了 Host 字段,用来指定服务器的域名。

HTTP 1.1 支持文件断点续传,RANGE:bytes,HTTP 1.0 每次传送文件都是从文件头开始,即 0 字节处开始。RANGE:bytes=XXXX 表示要求服务器从文件 XXXX 字节处开始传送,断点续传。即返回码是 206(Partial Content)

在 Chrome 中最多支持同时发出 6 个持久连接的 http 请求,这就限制了资源加载的速度,所以为了解决这个问题又推出了 http 2.0

# http 2.0

  • 二进制协议:HTTP 1.1 版的头信息肯定是文本(ASCII 编码),数据体可以是文本,也可以是二进制。HTTP 2.0 :则是一个彻底的二进制协议,头信息和数据体都是二进制,并且统帧(frame):头信息帧和数据帧。

  • 多工:HTTP 2.0 复用 TCP 连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了 "队头堵塞"(HTTP 2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP 1.1 大了好几个数量级)。

举例来说,在一个 TCP 连接里面,服务器同时收到了 A 请求和 B 请求,于是先回应 A 请求,结果发现处理过程非常耗时,于是就发送 A 请求已经处理好的部分, 接着回应 B 请求,完成后,再发送 A 请求剩下的部分。

  • 头信息压缩:HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 CookieUser Agent ,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。HTTP 2.0 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用 gzip 或 c ompress 压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

  • 服务器主动推送:HTTP 2.0 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。意思是说,当我们对支持 HTTP 2.0 的 web server 请求数据的时候,服务器会顺便把一些客户端需要的资源一起推送到客户端,免得客户端再次创建连接发送请求到服务器端获取。这种方式非常合适加载静态资源。 服务器端推送的这些资源其实存在客户端的某处地方,客户端直接从本地加载这些资源就可以了,不用走网络,速度自然是快很多的。

更新于 阅读次数