一、HTTP的实体数据
一.数据类型与编码
在 TCP/IP 协议栈里,传输数据基本上都是“header+body”的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。
HTTP 协议则不同,它是应用层的协议,数据到达之后工作只能说是完成了一半,还必须要告诉上层应用这是什么数据才行,否则上层应用就会“不知所措”。
1、MIME type
MIME 是一个很大的标准规范,但 HTTP 只“顺手牵羊”取了其中的一部分,用来标记 body 的数据类型,这就是我们平常总能听到的“MIME type”。
HTTP 里经常遇到的几个类别:
- text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本text/plain、样式表 text/css 等。
- image:即图像文件,有 image/gif、image/jpeg、image/png 等。
- audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。
- application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有application/json,application/javascript、application/pdf等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是application/octet-stream,即不透明的二进制数据。
2、Encoding type
HTTP 在传输时为了节约带宽,有时候还会压缩数据,为了不要让浏览器继续“猜”,还需要有一个“Encoding type”,告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。
Encoding type 常用的只有下面三种:
- gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
- deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
- br:一种专门为 HTTP 优化的新压缩算法(Brotli)。
二.数据类型使用的头字段
HTTP 协议为此定义了两个 Accept 请求头字段和两个 Content 实体头字段,用于客户端和服务器进行“内容协商”。也就是说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。
1、Accept
Accept 字段标记的是客户端可理解的 MIME type,可以用“,”做分隔符列出多个类型,让服务器有更多的选择余地。
Accept: text/html,application/xml,image/webp,image/png
这就是告诉服务器:“我能够看懂 HTML、XML 的文本,还有 webp 和 png 的图片,请给我这四类格式的数据”。
2、Accept-Encoding
Accept-Encoding 字段标记的是客户端支持的压缩格式,例如上面说的 gzip、deflate 等,同样也可以用“,”列出多个,服务器可以选择其中一种来压缩数据,实际使用的压缩格式放在响应头字段 Content-Encoding 里。
三.语言类型与编码
不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset,而服务器也不会发送 Content-Language,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language 字段,响应头里只会有 Content-Type 字段。
1、语言国际化问题
MIME type 和 Encoding type 解决了计算机理解 body 数据的问题,但互联网遍布全球,不同国家不同地区的人使用了很多不同的语言,虽然都是 text/html,但需要显示不同的语言文字。
2、语言类型使用的头字段
HTTP 协议也使用 Accept 请求头字段和 Content 实体头字段,用于客户端和服务器就语言与编码进行“内容协商”。
1.Accept-Language
Accept-Language 字段标记了客户端可理解的自然语言,也允许用“,”做分隔符列出多个类型,例如:
Accept-Language: zh-CN, zh, en
这个请求头会告诉服务器:“最好给我 zh-CN 的汉语文字,如果没有就用其他的汉语方言,如果还没有就给英文”。
2.Accept-Charset
客户端向服务器发送的请求字符集
3.Content-Language
服务器应该在响应报文里用头字段 Content-Language 告诉客户端实体数据使用的实际语言类型:
Content-Language: zh-CN
4、Content-Type
服务器向客户端发送的响应字符集类型
//浏览器请求 GBK 或 UTF-8 的字符集,然后服务器返回的是 UTF-8 编码,就是下面这样:
Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8
四.内容协商的结果
内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个 Vary 字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:
Vary: Accept-Encoding,User-Agent,Accept
这个 Vary 字段表示服务器依据了 Accept-Encoding、User-Agent 和 Accept 这三个头字段,然后决定了发回的响应报文。
四.手动测试
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Cookie: BAIDUID=4BDBC41C4B5A2DFC695B3A2D1D030D75:FG=1; BIDUPSID=4BDBC41C4B5A2DFC695B3A2D1D030D75; PSTM=1620955327; BD_UPN=12314753; __yjs_duid=1_2fd80adbd4702fa6fcbb85c5d18988501620981308254; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; BDUSS=3NseUp1NWJkYTNiOHBsMVR4ZjdWMDBnNkwtclFVcDhJYlVSRDV4WG9Bfkc0TXBnRVFBQUFBJCQAAAAAAAAAAAEAAADvPm0vydnE6rK7xNzN~LzHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMZTo2DGU6NgY; BDUSS_BFESS=3NseUp1NWJkYTNiOHBsMVR4ZjdWMDBnNkwtclFVcDhJYlVSRDV4WG9Bfkc0TXBnRVFBQUFBJCQAAAAAAAAAAAEAAADvPm0vydnE6rK7xNzN~LzHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMZTo2DGU6NgY; H_PS_PSSID=33984_33819_31254_34004_33675_33607_26350_33892; MCITY=-233%3A; delPer=0; PSINO=7; BD_HOME=1; BAIDU_WISE_UID=wapp_1621413683864_808; BDRCVFR[feWj1Vr5u3D]=I67x6TjHwwYf0; BD_CK_SAM=1; COOKIE_SESSION=8660_0_9_0_47_2_1_0_9_1_1_0_0_0_1_0_1621405720_0_1621414379%7C9%2385166_6_1619163364%7C3; ab_sr=1.0.0_NzIwODYwMWU0ZTljOTEwMGQ1Y2ZhNmMxNTYwNmEzZGI1ZGRmZjBjZDJlNDhiMjVhZjc4NmM2N2MxNGVlOWU4ZjgyZmMyZDg4ZjVhYWQzMjBiZTc1YWQzZTY1M2ZmZDFl; H_PS_645EC=e101aPRRpfAc7owjMeC4jyd5t2JTX3jwvTRm5OL844f9GGN2p5ldUFrg8do; sugstore=1; BA_HECTOR=2la5a1a104a18520k61ga9mj60r
Host: www.baidu.com
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
//User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
二、HTTP传输大文件的方法
一.数据压缩(适用于传输文本文件)
通常浏览器在发送请求时都会带着“Accept-Encoding”头字段,里面是浏览器支持的压缩格式列表,例如 gzip、deflate、br 等,这样服务器就可以从中选择一种压缩算法,放进“Content-Encoding”响应头里,再把原数据压缩后发给浏览器。
不过这个解决方法也有个缺点,gzip 等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用 gzip 处理也不会变小(甚至还有可能会增大一点),所以它就失效了。
二.分块传输
如果大文件整体不能变小,那就把它“拆开”,分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。
这样浏览器和服务器都不用在内存里保存文件的全部,每次只收发一小部分,网络也不会被大文件长时间占用,内存、带宽等资源也就节省下来了。
这种“化整为零”的思路在 HTTP 协议里就是“chunked”分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的 body 部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。
“Transfer-Encoding: chunked”和“Content-Length”这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked),这一点你一定要记住。
三.范围请求
HTTP 协议为了满足这样的需求,提出了“范围请求”(range requests)的概念,允许客户端在请求头里使用专用字段来表示只获取文件的一部分,相当于是客户端的“化整为零”。
范围请求不是 Web 服务器必备的功能,可以实现也可以不实现,所以服务器必须在响应头里使用字段“Accept-Ranges: bytes”明确告知客户端:“我是支持范围请求的”。
如果不支持的话该怎么办呢?服务器可以发送“Accept-Ranges: none”,或者干脆不发送“Accept-Ranges”字段,这样客户端就认为服务器没有实现范围请求功能,只能老老实实地收发整块文件了。
三、HTTP的连接管理
一.短连接
二.长连接
三.队头阻塞
四.性能优化
四、HTTP的重定向和跳转