扔物线 Android 高级课程笔记 - HTTP 原理和工作机制

277 阅读14分钟

HTTP(Hypertext Transfer Protocol,超文本传输协议)和 HTML(Hypertext Markup Language,超文本标记语言)一起诞生于 1989 年,由蒂姆 · 伯纳斯-李(Tim Berners - Lee)提出。用于在网络上请求和传输 HTML 内容

HTTP(Hypertext Transfer Protocol,超文本传输协议)

Hypertext:Hyper 有“超”和“扩展”的意思,Hypertext 可以叫做「超文本」或者「扩展型文本」,指 HTML 可以存在链向别的文本的链接,这种链接叫做 HyperLink,HyperLink 和 Hypertext 其实是一个意思。「超文本」其实就是 HTML,Markdown 本质上跟 HTML 是一样的,只不过 Markdown 的规则更简单。 Protocol:在中文中比较相近的词是“协议”,中文里面的协议指多方共同制定的一套规范,目的是更好的合作以保证合作各方的利益。而在计算机领域,Protocol 则有“通信协议”的意思,是一套通信的规则,这个规则明确了发送方要如何发送,接收方要如何接收,用来保证通信双方能够顺畅的沟通。 我们平常使用 HTTP 最直观的场景有在浏览器上打开一个网页,以及在 Android 中发起一个网络请求,然后获取响应。下图为打开一个网页后获取到的响应,这是 HTML 文件中的内容。

image.png

HTTP 工作机制

HTTP 工作的整个流程涉及到 2 个角色:客户端和服务器。客户端发起请求,接收服务器返回的响应。服务器接收客户端的请求,然后处理请求,将处理结果返回给客户端。

PC 端主要在浏览器中使用 HTTP,当在浏览其中输入网址,打开一个网页

  • 浏览器拼装 HTTP 请求报文,发送到 url 所指向的服务器
  • 服务器收到 HTTP 请求报文,完成业务逻辑处理,将处理结果封装到 HTTP 响应报文中,返回给浏览器
  • 浏览器收到响应报文,通过浏览器内核(也叫渲染引擎)将响应内容渲染成一个用户可见的文字或者图像,网页就展示出来了。

Chrome 内核是 Blink,Safari 内核是 Webkit,firefox 内核是 Gecko,IE 最早用的是自己的内核,最新版的 Edge 用的 Blink 内核。Blink 最早也是基于 Webkit。 浏览器内核就是渲染引擎,它控制了 HTML 的显示,包括如何布局,如何提高渲染速度以及如何减少性能损耗

移动端则是在 app 中发起一个网络请求,流程与 PC 端类似,此时客户端变成了移动设备,移动设备在收到服务器返回的响应报文后,执行数据处理和保存,用最新的数据更新界面。

URL & HTTP 报文

URL 有三个部分:协议类型、服务器地址(和端口号)、路径。格式:协议类型://服务器地址[:端口号]路径。例如:github.com/search?q=an…,https:// 是协议,github.com 是服务器地址,search?q=android 是路径

1. 请求报文

客户端(浏览器或 Android 设备)会将 URL 解析成请求报文,下图使用得是 Postman 查看到的请求报文 image.png

  • GET /search?q=android HTTP/1.1 是请求行。
    • GET:请求类型,GET 表示取数据。如果是要添加信息,比如添加一个用户,则是 POST
    • /search?q=android :路径,指明要访问哪里的资源,这是给服务器看的
    • HTTP/1.1:这是 HTTP 版本号,HTTP 主要版本号有 0.9、1.0、1.1、2.0,0.9 和 1.0 现在已经基本被废弃了,2.0 则还没有被广泛应用到浏览器上,目前 Web 服务主要使用的是 HTTP/1.1
  • "Host: github.com" 是 Header,冒号左边是 key,右边是 value,这句 Header 是给服务器所在的主机看的。除了这个,还有其他 Header,每个 Header 占一行,例如 "Content-Type: text/plain","Content-Length: 243",上图所示的请求报文中,GET 请求行之后,都是 Header
  • body:这是正文部分,一般 POST 请求会有 body,请求行里面的路径使用来定位资源,服务器拿这些 body 去做相应的数据处理,比如添加用户,body 里面就是新用户的信息

HTTP 的更新需要服务器和客户端同时更新,因此新版本的普及会相对慢一些。而浏览器和 app 相比,浏览器更新的也会慢一些,因为客户浏览器的更新往往是比较慢的,而 app 和接受 app 请求的服务器端往往是同一家公司开发的,升级 HTTP2 只需要客户端工程师和后端工程师相互协调一下就可以。

2. 响应报文

服务器收到相应报文,完成相应的处理之后,返回给客户端一个响应报文,响应报文的主要区别是第一行,请求报文的第一行是请求行,响应报文的第一行是状态行 image.png

  • HTTP/1.1 200 OK 是状态行,状态行之后之后的多行内容则是 Headers,每一行都是一个 Header。
    • 200 是状态码,表示响应成功。状态码是对响应状态的简单描述
    • OK 则是对状态码的描述,主要是给开发人员看的。

HTTP 请求方法和状态码

1. HTTP 请求方法

  • GET:用于获取资源,GET 请求方法的请求报文没有 Body。GET 方法也是 HTTP/0.9 的唯一一个方法。具有幂等性。
  • POST:用于增加或修改资源,新的资源放在 Body 中。不具有幂等性,因为发送多次 POST 请求,服务器会执行多次修改,每次请求结果是不一样的。
  • PUT:用于修改资源,新的资源信息放在 Body 中,它的作用和 POST 方法类似,用哪个都可以,主要看服务器提供了哪些接口。具有幂等性,发送多次修改的最终结果都是一样的。
  • DELETE:用于删除资源,没有 Body(只要把需要删除的资源定位到就可以了,并不需要 Body)。具有幂等性
  • HEAD:它和 GET 一样,也是用于获取资源的,不过获取到的服务器响应没有 Body,一般用于向服务器获取一些状态信息,比如在真正执行 GET 请求之前,发送一个 HEAD 请求,获取要获取的数据有多大,是否支持断点续传、分段下载等等。

一个方法是否幂等指,调用该方法一次和调用多次的结果是否一致。

2. 状态码

状态码主要是提供给开发人员看的,方便后端人员调试。它主要分 1xx、2xx、3xx、4xx、5xx 五类

  • 1xx:这类为临时消息。主要有 100「继续发送」和 101「正在切换协议」
    • 100:当客户端需要往服务器上传比较大的数据时,往往会拆成多个请求,客户端会在这些请求加上一个 “Expect: 100-continue” 的 Header,服务器在收到这些请求后,不会立刻处理请求,而是返回一个状态码为 100 的响应报文,告诉客户端「请你继续发送」,客户端继续发送,直到最后一个请求(不带 Expect: 100-continue)发送到服务器,服务器会开始处理请求,请求完毕之后返回状态码为 200 的响应报文。
    • 101:一般用于 HTTP 协议的切换。当客户端不知道服务器是否支持 HTTP/2.0 时,客户端先发送一个 HTTP/1.1 的请求,请求中带“Upgrade: h2c”,去询问服务器是否支持 HTTP/2.0,如果服务器支持 HTTP/2.0,会返回状态码为 101 的响应报文,表示自己支持 HTTP/2.0。如果服务器不支持 HTTP/2.0,则 “Upgrade: h2c” 会被忽略,服务器直接返回 HTTP/1.1 200 OK,这时客户端就知道服务器不支持 HTTP/2.0,之后继续使用 HTTP/1.1 发送请求。
  • 2xx:请求成功。200(OK),201(创建成功)
  • 3xx:重定向。301(永久移动)、302(暂时移动)、304(内容未改变)。服务器返回的响应里面会包含一个 url,也就是客户端实际应该访问的 url
    • 301:表示资源永久迁移
    • 302:表示资源临时迁移。浏览器对于 301 和 302 这两个状态码的响应是一样的。它们的区别在于,对于永久迁移,浏览器会将旧网站的的数据(点击数据,权重等)都迁移到新的网站。而临时迁移则不会有这些行为,只是临时的去重定向到一个新的网站,一般用于网站正在备案,完成一些修改的情况。
    • 304:客户端发送请求时带上一个 Etag 的 Header ,它的值是上一次请求获取到的内容的一个标签(也可以叫 etag、指纹、hash),服务器根据这个标签可以判断自上次请求来,这部分内容是否发生了改变,如果没有改变,服务器就返回客户端 304 这个状态码,告诉客户端直接使用缓存。
  • 4xx:客户端错误。即客户端发起的这个请求本身是错的,服务器无法识别,或者服务器认为请求的东西根本不存在。比如 400(客户端请求错误)、401(请求未授权)、403(请求被禁止)、404(找不到内容)
  • 5xx:服务器错误。这类错误不是给用户看的,而是给服务器开发者看的。比如 500(服务器内部错误)

HTTP Header 和 Body

1. Header

Header 是 HTTP 请求的元数据,也可以称为 HTTP 请求的属性,一般用于描述 body 有多长,用什么格式发,数据有没有压缩,用什么格式压缩等信息。

  • Host:目标服务器地址。这是给目标主机看的,用于确定这个报文要发到哪个服务器程序上。一台主机是可以运行多个服务器程序的,主机通过 Host 则可以确认这个报文是属于哪个服务器程序。IP 地址查找则是发生在这之前,是发生在请求发送之前,通过 “DNS(Domain Name System) 查询” 的查找域名对应的 IP 地址。

用户往浏览器输入 url 之后,浏览器会向 DNS 服务器询问这个 url 对应的 IP 地址,然后浏览器才会往这个 IP 地址去发送请求。

  • Content-Type:Body 的类型。浏览器通过 Content-Type 确定以什么逻辑来解析 Body,常见的 Content-Type 有以下几种
    • text/html:html 文本,也就是我们在浏览器上经常见到的网页。
    • application/x-www-form-urlencode:普通表单,这是一个纯文本格式的表单,使用 url 相同的编码格式对表单内容进行编码,装进 Body 中。
    • multipart/form-data:将表单分成多个部分传输,这些表单都在同一个请求内。当提交的内容包含二进制文件时,一般会用这种 Body 类型。这也是图片上传的主流方法。不同的部分使用一个特定的长串进行分割,因为包含二进制内容,使用简单的字符串容易与表单内容重复。使用这种方式会增加数据包的大小,因为增加了分割长串
    • application/json:Body 部分使用 Json 格式的数据。使用 Json 可以对数据进行多层分级,它的扩展性要比 url 更强。
    • image/jpeg、application/zip:Web API 响应的数据,POST 和 PUT 请求上传文件。我们下载文件,服务器就会使用这种格式。这是一种很好的上传单文件的格式
  • Content-Length:Body 的长度。网络上传输的数据都是以二进制的形式存在的,而二进制是什么内容都有可能存在的,因此无法设定某个值作为结束标志
  • Transer-Encoding:chunked。表示服务器要使用分块传输编码(Chunked Transfer Encoding),一般用于服务器获取数据比较耗时的情况,将先获取到的部分数据传给客户端,让用户得到及时的响应。使用了这个 Header ,就不会有 Content-Length 了,客户端会通过特殊字符来判断是否已经读取结束。

问题:分块传输的最后一个块的响应,是否还是使用 Transfer-Encoding 这个 Header

  • Location:重定向的目标 url。客户端收到 3xx 的状态码时,会从 Location 这个 Header 获取到重定向的目标 url ,然后对这个 url 发起一次 HTTP 请求。
  • User-Agent:用户代理,用于告诉服务器,是什么设备和什么类型的软件发起的 HTTP 请求(比如手机浏览器,手机 app 等),然后服务器会返回适配了该设备和这类软件特性的网页内容。现在浏览器发起的请求 User-Agent 一般都会带 “Mozilla/5.0”
  • Range:用于请求报文,表示请求获取哪段数据,值的格式一般是:bytes=-。
  • Accept-Range:用于响应报文。表示支持对哪种类型的数据进行分段传输,比如 Accept-Range: bytes 表示支持对字节类型的数据进行分段传输。一般用于断点续传和多线程下载,需要看服务器是否支持。
  • Content-Range:用于响应报文,描述当前传过来的数据的范围,以及总数据量。
  • Cookie 和 Set-Cookie:发送 Cookie 和设置 Cookie
  • Authorization:客户端的授权信息,一般用于请求报文
  • Accept:客户端能接受的数据类型,比如 text/html
  • Accept-Charset:客户端能接受的字符集,比如 utf-8
  • Accept-Encoding:客户端能接受的编码类型,比如 gzip
  • Content-Encoding:body 所使用的编码格式,比如 gzip

2. Cache 相关的 Header

Cache(缓存) 和 Buffer(缓冲) 的区别:Cache 是将已经用过的内容暂时存起来,稍后在需要使用时再拿出来用。Buffer 一般是基于“生产者-消费者”工作流,在生产时生产多一部分内容,暂时找个地方放着,稍后有消费这来时,直接从这个地方取内容,这些内容是未使用过的

Cache 相关的 Header 有:

  • Cache-Control:可选值有 no-cache、no-store、max-age
  • Last-Modified:
  • If-Modified-Since:用于请求报文。
  • Etag:用于响应报文,是响应报文正文部分的标签(也可以叫快照,Etag、指纹、hash)。
  • If-None-Match:
  • Cache-Control:可选值有private 、public

REST(Representational state transfer)

维基百科对 REST 的描述:A softerware architectural style that describes the architecture of the Web. 中文翻译过来,REST 其实就是一个 Web 软件架构风格,主要用于指导后端接口的设计。

REST 定义了以下约束:

  • Client-server architecture:客户端-服务器架构,即 C/S 架构。B/S 本质上也是 C/S,只是区分了浏览器客户端(网页)和实际的应用程序客户端而已。
  • Statelessness:无状态,即每次请求都是独立的,请求本身需要带上请求者的身份信息。
  • Cacheability:可缓存
  • Layered system:分层系统。客户端不用关心它的请求具体是发送到哪台机器上,它只负责发送请求,然后获取响应就可以了。
  • Code on demand:服务器可向客户端传输可执行代码(如 JS)来扩展或自定义客户端的功能
  • Uniform interface:统一接口,这项约束有以下四项子约束
    • Resource identification in requests:通过请求可定位到服务器资源(通过 url 可定位到要访问的服务器资源)
    • Resource manipulation through representation:通过服务器返回来的表现内容可操作服务器资源(POST / PUT / DELETE)
    • Self-descriptive messages:自描述消息,就是传输的消息本身需要说明这是一个什么类型的消息,HTTP 报文中的 Content-Type 就是这个作用
    • Hypermedia as the engine of application state(HATEOAS):服务器下发的表现内容需要能对客户端起引导作用,客户端通过引导能够浏览网站所有的内容以及找到自己想要访问的内容,比如网站的首页。

HTTP 其他概念

HTTP 的无连接是一种性质,跟它的无状态是一样的。无状态指的是服务器不会去专门记录连接的状态。无连接则指的是服务器不会专门维持一个和客户端保持认识的连接(即每次请求,服务器和客户端都会重新认识一次,除非这个连接是之前缓存过的连接)

抓包工具:WireShark。

Java 中的 Socket 其实是一个被封装好的 TCP 工具。