美文网首页
HTTP协议详解(精心整理)

HTTP协议详解(精心整理)

作者: 吃大米的小蚂蚁 | 来源:发表于2020-08-13 11:12 被阅读0次

    HTTP(Hyper Text Transfer Protocol)<超文本传输协议>

    在TCP/IP协议模型中, HTTP协议是在应用层使用的协议,他的作用是将我们需要发送的数据进行封装,然后通过下一层的层层传递发送给目的主机,目的主机会给他一个响应或者返回他查询的数据。应该是在我们开发的过程中使用到最多的一个协议。本篇文章主要对HTTP协议做一个尽可能详细的介绍,方便各位浏览也方便自己日后有问题查阅。

    HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程:

    1. 浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
    2. 解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接;
    3. 浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器;
    4. 服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;
    5. 释放 TCP连接;
    6. 浏览器将该 html 文本并显示内容;

    客户与服务器之间的HTTP连接是一种一次性连接,它限制每次连接只处理一个请求,当服务器返回本次请求的应答后便立即关闭连接,下次请求再重新建立连接。这种一次性连接主要考虑到WWW服务器面向的是Internet中成干上万个用户,且只能提供有限个连接,故服务器不会让一个连接处于等待状态,及时地释放连接可以大大提高服务器的执行效率

    从技术上讲是客户在一个特定的TCP端口(端口号一般为80)上打开一个套接字。如果服务器一直在这个周知的端口上倾听连接,则该连接便会建立起来。然后客户通过该连接发送一个包含请求方法的请求块。

    目前HTTP的版本有 0.9,1.0,1.1,2.0

    使用最多的版本还是HTTP/1.1

    HTTP协议有以下几个特点:

    1.HTTP协议是基于TCP/IP协议模型的,所以我们前面文章介绍过的TCP的慢启动就自然而然的反应在了每一个HTTP的数据交互里,当一个页面有很多小文件发起的请求的时候,这些请求都会经历慢启动,花费的时间就会比较多。

    2.TCP的多条链接同时发起引起带宽竞争,带宽固定,所以可能想快速下载的资源反而下载的慢。

    3.一条HTTP建立的链接只有处理了一个请求才可以处理下一个TCP请求,回有头部阻塞的情况出现。HTTP/2 进一步解决线头阻塞问题。通过独立不同流,让各个流之间实现相互独立传输,互不干扰.

    4.无状态保存:自身不对某次请求和响应之间的通信状态做保存,不做持久化处理。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成 如此简单的。但是随着WEB的发展,很多时候需要携带状态,后来HTTP协议引入了cookie功能,后面会详细介绍。

    5.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间,并且可以提高并发性能,不能和每个用户建立长久的连接,请求一次相应一次,服务端和客户端就中断了。但是无连接有两种方式,早期的http协议是一个请求一个响应之后,直接就断开了,但是现在的http协议1.1版本不是直接就断开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操作,如果用户在这几秒钟之内有新的请求,那么还是通过之前的连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,那么就会断开连接,这样可以提高效率,减少短时间内建立连接的次数,因为建立连接也是耗时的,默认的好像是3秒种现在,但是这个时间是可以通过后端的代码来调整的,自己网站根据自己网站用户的行为来分析统计出一个最优的等待时间。

    HTTP请求报文解析

    第一部分 请求行
    由【请求方法 空格 URI 空格 HTTP版本 换行符】组成

    比如:

    GET /slide_1_86523_474747.html HTTP/1.1

    GET 为请求方法

    /slide_1_86523_474747.html 为URI

    HTTP/1.1 为HTTP版本

    第二部分 请求头部,用来说明服务器要使用的附加信息,有很多种参数可以选择(后面会有详细的参数表)

    第三部分 是一个空行

    第四部分是请求报文的主体

    下面是一个请求的截图,可以对比看下:

    这是一个HTTP请求报文的截图。把里面的数据拿出来整理一下就是下面的格式

    GET /slide_1_86523_474747.html HTTP/1.1

    Host: slide.news.sina.com.cn

    Cookie: lxlrttp=1578733570; ULV=1597218435568:91:11:3:61.52.165.250_1597218193.555396:1597218193465;

    U_TRS2=000000a0.14675b80.5f339e83.291ef996; Apache=61.52.165.250_1597218193.555396; _ga=GA1.3.1348710685.1578706684; _gid=GA1

    Connection: keep-alive\r\n

    Upgrade-Insecure-Requests: 1\r\n

    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8\r\n

    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.5 Safari/605.1.15\r\n

    Referer: https://www.sina.com.cn/\r\n

    Accept-Language: zh-cn\r\n

    Accept-Encoding: gzip, deflate\r\n

    HTTP响应报文解析

    第一行:【HTTP版本 空格 状态码 空格 状态码描述 换行符】组成

    第二行:返回的各种参数,后面会有表格对应查询

    第三行:一个固定的换行符

    第四行:返回的数据


    知识点整理

    HTTP协议看似简单,实际上也是比较复杂的,扩展性比较强,下面整理了一些个人感觉比较重要的知识点,很多来自参考的文章整理,自己对比写了一遍加深印象,也有很多感觉写的实在很好的直接拿过来用了,各位可以选择性的浏览。

    请求方法

    6 DELETE.png

    请求体参数表

    (由https://www.jianshu.com/p/7c8b4576e4bb 整理,很详细,直接拿过来了,方便以后查阅)

    HTTP响应的状态码

    3xx DED-BADALIHITED- $00IIE.png

    Content-Type 字段

    关于字符的编码,1.0版规定,头信息必须是 ASCII 码,后面的数据可以是任何格式。因此,服务器回应的时候,必须告诉客户端,数据是什么格式,这就是Content-Type字段的作用。

    下面是一些常见的Content-Type字段的值。


    这些数据类型总称为MIME-TYPE,每个值包括一级类型和二级类型,之间用斜杠分隔。

    除了预定义的类型,厂商也可以自定义类型。

    MIME type还可以在尾部使用分号,添加参数。

    Content-Type: text/html; charset=utf-8

    上面的类型表明,发送的是网页,而且编码是UTF-8。

    客户端请求的时候,可以使用Accept字段声明自己可以接受哪些数据格式。

    Accept: /

    上面代码中,客户端声明自己可以接受任何格式的数据。

    MIME type不仅用在HTTP协议,还可以用在其他地方,比如HTML网页。

    内容压缩传输

    HTTP压缩是指: Web服务器和浏览器之间压缩传输的”文本内容“的方法。 HTTP采用通用的压缩算法,比如gzip来压缩html,javascript, CSS文件。 能大大减少网络传输的数据量,提高了用户显示网页的速度。当然,同时会增加一点点服务器的开销。 首先,浏览器发送一个请求(request)给web服务器,支持一个压缩格式如(gzip),服务端会将原来的源码压缩之后,通过http响应(response)信息返回给web浏览器,浏览器接收之后,显示出来。

    内容编码:通过content-encoding来指定内容的压缩方式,通过content-length来指定文件大小,服务端会在缓冲指定大小的数据后才发送到浏览器,浏览器收到指定大小的数据后认为文件已接收完毕,之后用相应的压缩方式对内容进行解压。

    传输编码:上述方式中服务器等指定大小的资源缓冲好才发给浏览器,造成浏览器等待时间长,会影响用户体验。可通过transfer-encoding:chunked来改变这一行为:服务器会将文件内容分块传输给浏览器而不用等所有缓冲完,最后一块大小为0,指示浏览器只要收到数据就进行处理展示而不用等所有收到。

    content-encoding和transfer-encoding通常结合使用

    http压缩对纯文本可以压缩至原内容的40%, 从而节省了60%的数据传输。

    在http协议中,可以对内容(也就是body部分)进行编码, 可以采用gzip这样的编码。 从而达到压缩的目的。 也可以使用其他的编码把内容搅乱或加密,以此来防止未授权的第三方看到文档的内容。

    压缩过程:

    1. 浏览器发送Http request 给Web服务器, request 中有Accept-Encoding: gzip, deflate。 (告诉服务器, 浏览器支持gzip压缩)

    2. Web服务器接到request后, 生成原始的Response, 其中有原始的Content-Type和Content-Length。

    3. Web服务器通过Gzip,来对Response进行编码, 编码后header中有Content-Type和Content-Length(压缩后的大小), 并且增加了Content-Encoding:gzip. 然后把Response发送给浏览器。

    上面截图的服务器响应也有对应的压缩字段,可以对比看下。


    Content-Encoding 字段

    HTTP可以对发送的数据进行压缩,这个字段就是说明压缩使用的方法

    Content-Encoding: gzip

    一种数据格式,默认且目前仅使用deflate算法压缩data部分。

    缺点:

    JPEG这类文件用gzip压缩的不够好。

    简单来说, Gzip压缩是在一个文本文件中找出类似的字符串, 并临时替换他们,使整个文件变小。这种形式的压缩对Web来说非常适合, 因为HTML和CSS文件通常包含大量的重复的字符串,例如空格,标签。

    Content-Encoding: compress 表明实体采用Unix的文件压缩程序

    Content-Encoding: deflate deflate是一种压缩算法,是huffman编码的一种加强

    Content-Encoding: identity 表明没有对实体进行编码。当没有Content-Encoding header时, 就默认为这种情况

    客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。比如:

    Accept-Encoding: gzip, deflate

    http压缩对纯文本可以压缩至原内容的40%, 从而节省了60%的数据传输。

    浏览器是不会对Request压缩的。 但是 一些HTTP程序在发送Request时,会对其进行编码。

    持久连接

    HTTP/1.0 版的主要缺点是,每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。

    TCP连接的新建成本很高,因为需要客户端和服务器三次握手,并且开始时发送速率较慢(slow start)。所以,HTTP 1.0版本的性能比较差。随着网页加载的外部资源越来越多,这个问题就愈发突出了。

    为了解决这个问题,有些浏览器在请求时,用了一个非标准的Connection字段。

    Connection: keep-alive

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

    Connection: keep-alive

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

    1.1 版的最大变化,就是引入了持久连接(persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。

    客户端和服务器发现对方一段时间没有活动,就可以主动关闭连接。不过,规范的做法是,客户端在最后一个请求时,发送Connection: close,明确要求服务器关闭TCP连接。

    Connection: close

    目前,对于同一个域名,大多数浏览器允许同时建立6个持久连接。

    管道机制

    1.1版本引入了管道机制,在同一个TCP连接里,客户端可以同时发送多个请求,这样就进一步改善了HTTP协议的效率。

    举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求。管道机制则是允许浏览器同时发出A请求和B请求,但是服务器还是按照顺序,先回应A请求,完成后再回应B请求。

    Content-Length 字段

    一个TCP连接现在可以传送多个回应,势必就要有一种机制,区分数据包是属于哪一个回应的。这就是Content-length字段的作用,声明本次回应的数据长度。

    Content-Length: 3495

    上面代码告诉浏览器,本次回应的长度是3495个字节,后面的字节就属于下一个回应了。

    分块传输编码

    使用Content-Length 字段的前提是,服务器发送回应之前,必须知道回应的数据长度,

    对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。更好的处理方法是,产生一块数据,就发送一块,采用"流模式"(stream)取代"缓存模式"(buffer)。

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

    每个非空的数据块之前,会有一个16进制的数值,表示这个块的长度。最后是一个大小为0的块,就表示本次回应的数据发送完了。下面是一个例子。


    队头阻塞

    虽然1.1版允许复用TCP连接,但是同一个TCP连接里面,所有的数据通信是按次序进行的。服务器只有处理完一个回应,才会进行下一个回应。要是前面的回应特别慢,后面就会有许多请求排队等着。这称为"队头堵塞"(Head-of-line blocking)。

    为了避免这个问题,只有两种方法:一是减少请求数,二是同时多开持久连接。这导致了很多的网页优化技巧,比如合并脚本和样式表、将图片嵌入CSS代码、域名分片(domain sharding)等等。如果HTTP协议设计得更好一些,这些额外的工作是可以避免的

    数据的分块传输

    有一些文件可以使用gzip进行压缩,但是视频就不可以了,这个时候我们就需要进行分块传输


    在报文中使用"Transer-Encoding:chunked"表示,代表body部分数据是分块传输的。

    在body中存在一个content-length字段表示body的长度,两者不能共存,另外很多时候是流式数据,body中没有指明content-length,这个时候一般就是chunked传输了。

    分块运输的编码格式:

    <1> 每一个分块包含长度和数据块

    <2> 长度头按照CRLF结束

    <3> 数据块在长度快后,且最后CRLF结尾

    <4> 使用长度0表示结束,"0\r\n\r\n"

    如下图


    截断发送

    分块解决了咋们一部分问题,但是有的时候我们想截断发送怎么办呢。

    在HTTP中提供了使用字段“Accept - Ranges: bytes”,明确告知客户端:“我是支持范围请求的”,Range从0开始计算,比如Range:0-5则读取前6个字节,服务器收到了这个请求,将如何回应呢

    <1> 合法性检查。比如一共只有20字节,但是请求range:100-200。此时会返回416----"范围请求有误"

    <2> 范围正常,则返回216,表示请求数据知识一部分

    <3> 服务器端在相应投资端增加Content-Range,格式"bytes x-y/length"。

    断点续传的实现思路:

    <1> 查看服务器是否支持范围请求并记录文件大小

    <2> 多个线程分别负责不同的range

    <3> 下载同时记录进度,即使因为网络等原因中断也没事,Range请求剩余即可

    Cookie机制

    HTTP是无状态、无记忆的,Cookie机制的出现让其有记忆功能,简单流程如下:


    Cookie是由浏览器负责存储,换个浏览器打开网页的话之前沟通的Cookie就不会携带了。

    Cookie常见的应用一个是身份识别,一个是广告追踪,比如我们在访问网页视频或者图片的时候,广告商会悄悄给我们Cookie打上标记,方便做关联分析和行为分析,从而给我推荐一些相关内容。

    HTTP代理

    之前说的一一对一的问答情景,但是很多情况下也存在多台服务器进行通信服务,比较常见的一个做法就是在请求方与问答方中间增加一个中间代理。


    代理作为中间位置,相对青请求方为服务端,相对于后端为请求方。

    代理常见的功能未负载均衡,在负载均衡中需要区分正向代理与反向代理,其中也就会涉及调度算法,比如轮询,一致性哈希等。


    扩展:

    HTTP/2

    HTTP/2 的前世是HTTP1.0和HTTP1.1。虽然之前仅仅只有两个版本,但这两个版本所包含的协议规范之庞大,足以让任何一个有经验的工程师为之头疼。网络协议新版本并不会马上取代旧版本。实际上,1.0和1.1在之后很长的一段时间内一直并存,这是由于网络基础设施更新缓慢所决定的。

    2015年,HTTP/2 发布。它不叫 HTTP/2.0,是因为标准委员会不打算再发布子版本了,下一个新版本将是 HTTP/3。

    二进制协议

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

    二进制协议的一个好处是,可以定义额外的帧。HTTP/2 定义了近十种帧,为将来的高级应用打好了基础。如果使用文本实现这种功能,解析数据将会变得非常麻烦,二进制解析则方便得多。

    多工

    HTTP/2 复用TCP连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应,这样就避免了"队头堵塞"。

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

    这样双向的、实时的通信,就叫做多工(Multiplexing)。

    数据流

    因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。

    HTTP/2 将每个请求或回应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流ID,用来区分它属于哪个数据流。另外还规定,客户端发出的数据流,ID一律为奇数,服务器发出的,ID为偶数。

    数据流发送到一半的时候,客户端和服务器都可以发送信号(RST_STREAM帧),取消这个数据流。1.1版取消数据流的唯一方法,就是关闭TCP连接。这就是说,HTTP/2 可以取消某一次请求,同时保证TCP连接还打开着,可以被其他请求使用。

    客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

    头信息压缩

    HTTP 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如Cookie和User Agent,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度。

    HTTP/2 对这一点做了优化,引入了头信息压缩机制(header compression)。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

    服务器推送

    HTTP/2 允许服务器未经请求,主动向客户端发送资源,这叫做服务器推送(server push)。

    常见场景是客户端请求一个网页,这个网页里面包含很多静态资源。正常情况下,客户端必须收到网页后,解析HTML源码,发现有静态资源,再发出静态资源请求。其实,服务器可以预期到客户端请求网页后,很可能会再请求静态资源,所以就主动把这些静态资源随着网页一起发给客户端了。

    参考阅读:

    HTTP 传输内容的压缩

    百度百科

    HTTP请求报文头属性

    HTTP协议详解

    HTTP协议超级详解

    HTTP 协议入门

    关于HTTP协议,一篇就够了

    一文彻底拿下HTTP/HTTPS协议

    相关文章

      网友评论

          本文标题:HTTP协议详解(精心整理)

          本文链接:https://www.haomeiwen.com/subject/bnlbdktx.html