HTTP之三:HTTP报文详解

2020-05-25  本文已影响0人  longLiveData

本文仅供笔者平日学习笔记之用,侵删
原文:https://mp.weixin.qq.com/s/wZONAYSlkufds0LMzoE-9g

报文包括:

客户端与服务端进行交互的信息为报文。客户端为请求报文,服务端为响应报文。我们先用Wireshark抓一个博客看看。

Wireshark(前称Ethereal)是一个网络封包分析软件。网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料。Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换。

GET /article/12 HTTP/1.1
Host: www.xxx.cn
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: SESSION=so9nlsvenminor5abs65sh9dsa

HTTP/1.1 200 OK
Server: nginx
Date: Sun, 17 May 2020 17:04:29 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: blade-2.0.6-BETA
Content-Encoding: gzip

1、请求报文

请求报文通常由三部分组成:

请求方法:一共有八种方法选择,如下图所示。采用不同的方法获取不同的资源:

其中非常常见的几种请求方法:

2、URI

统一资源标识符(Uniform Resource Identifier),严格来说不等于网址,它包含URL和URN,可是URL太出名了以致于URL="网址"。无论开发,测试运维配置都离不开URI。

网络层的IP主要目的是解决路由和寻址。现在的IP地址按照"."分割,总共2的32次方大约42亿。对于计算机来说比较方便,但是对于人类来说还是不容易记忆,此时出现DNS了,他把IP地址映射为我们平时常见的"redis.org",按照"."分割域名,从左到右级别越高,最右边为"顶级域名"。如下图所示:

好了,现在TCP提供可靠(数据不丢失)且字节流(数据完整性),而且也有方便我们记忆的域名,但是互联网资源千万种,也不知道访问什么(图片,文字,视频一大堆),这个时候URI(统一资源标识符)出现了,那长啥样?

image

看几个例子:
http://nginx.org/en/download.html
file:///E:/Demo/index/

这里注意是三个"///",因为前面"://"作为分隔符,资源路径按照"/"开头。

既然规则这么多,对于接收方而言需要完成的解析也需要遵守规则,全球用户很多使用HTTP,每个国家地区所使用语言不同,HTTP为了能对其进行统一处理,引入了URI编码,方法比较简单,将非ASCII或者特殊字符全部转换为十六进制字节值,同时在前面加入"%"。比如空格被转换为"%20","中国"就编码为"%E4%B8%AD%E5%9B%BD%0A"。

3、响应报文

状态行----服务器响应的状态。

<1>版本号:使用的HTTP什么版本

<2>状态码:不同数字代表不同的结果,就如我们在编码时,通过返回不同的值代表不同的语义。

**状态码一共分为5类。

"200OK"
最常见的成功状态码,表示一切正常,客户端获得期许的处理结果。如果不是Head请求,那么在响应头中通常会有Body数据。
"204No Content"
这个的含义和"200"很相似,不同之处在于它的响应头中没有Body数据
"206Partial Content"
是 HTTP 分块下载或断点续传的基础,在客户端发送“范围请求”、要求获取资源的部分数据时出现,它与 200 一样,也是服务器成功处理了请求,但Body 里的数据不是资源的全部,而是其中的一部分。状态码 206 通常还会伴随着头字段“Content-Range”,表示响应报文里 Body 数据的具体范围,供客户端确认,例如“Content-Range: bytes 0-99/5000”,意思是此次获取的是总计5000 个字节的前 100 个字节。

"301Moved Permanently"
“永久重定向”,意思是本地请求的资源以及不存在,使用新的URI再次访问。
“302Found”
“MovedTemporarily”,“临时重定向”,临时则所请求的资源暂时还在,但是目前需要用另一个URI访问。
301和 302 通过在字段Location中表明需要跳转的URI。两者最大的不同在于一个是临时改变,一个是永久改变。举个例子,有时候需要将网站全部升级为HTTPS,这种永久性改变就需要配置永久的"301"。有时候晚上更新系统,系统暂时不可用,可以配置"302"临时访问,此时不会做缓存优化,第二天还会访问原来的地址。
“304Not Modified”
运用于缓存控制。它用于 If-Modified-Since 等条件请求,表示资源未修改,可以理解成“重定向已到缓存的文件”(即“缓存重定向”)。

"400Bad Request"
通用错误码,表示请求报文有错误,但是这个错误过于笼统。不知道是客户端还是哪里的错误,所以在实际应用中,通常会返回含有明确含义的状态码。
“403Forbidden”:
注意了,这一个是表示服务器禁止访问资源。原因比如涉及到敏感词汇、法律禁止等。当然,如果能让客户端有一个清晰的认识,可以考虑说明拒绝的原因并返回即可。
“404Not Found”
这可能是我们都知道且都不想看到的状态码之一,它的本意是想要的资源在本地未找到从而无法提供给服务端,但是现在,只要服务器"耍脾气"就会给你返回 404,而我们也无从得知后面到底是真的未找到,还是有什么别的原因
"405Method Not Allowed"
获取资源的方法好几种,我们可以对某些方法进行限制。例如不允许 POST 只能 GET
"406Not Acceptable"
客户端资源无法满足客户端请求的条件,例如请求需要中文但只有英文
"408Request Timeout"
请求超时,服务器等待了过长的时间
"409Conflict"
多个请求发生了冲突,可以理解为多线程并发时的竞态
413Request Entity Too Large
请求报文里的 body 太大
414Request-URI Too Long
请求行里的 URI 太大
429Too Many Requests
客户端发送了太多的请求,通常是由于服务器的限连策略
431Request Header Fields Too Large
请求头某个字段或总体太大;

“500Internal Server Error”
和400类似,属于一个通用的错误码,但是服务器到底是什么错误我们不得而知。其实这是好事,尽量少的将服务器资源暴露外网,尽量保证服务器的安全。
“502Bad Gateway”
通常是服务器作为网关或者代理时返回的错误码,表示服务器自身工作正常,访问后端服务器时发生了错误,但具体的错误原因也是不知道的。
“503Service Unavailable”
表示服务器当前很忙,暂时无法响应服务,我们上网时有时候遇到的“网络服务正忙,请稍后重试”的提示信息就是状态码 503。
503是一个“临时”的状态:暂时比较忙,稍后提供服务。在响应报文中的“Retry-After”字段,指示客户端可以在多久以后再次尝试发送请求。

4、请求体

上面大部分都是涉及到Header部分,还有非常重要的Body,Everybody。

头字段注意事项:

<1> 字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好;
<2> 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线"_"。例如,“test-name”是合法的字段名,而“test name”“test_name”是不正确的字段名;
<3> 字段名后面必须紧接着“:”,不能有空格,而":"后的字段值前可以有多个空格;
<4> 字段的顺序是没有意义的,可以任意排列不影响语义;
<5> 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。

HTTP的body常常被分为这几种的类别:

<1> text:超文本text/html,纯文本text/plain
<2> audio/video:音视频数据
<3> application: 可能是文本,也可能是二进制,交给上层应用处理
<4> image: 图像文件。image/png等

但是带宽一定,数据大了通常考虑使用压缩算法进行压缩,在HTTP中使用Encoding type表示,常用的压缩方式有下面几种:

<1> gzip:
一种数据格式,默认且目前仅使用deflate算法压缩data部分。
<2>deflate:
deflate是一种压缩算法,是huffman编码的一种加强。
<3>br:
br通过变种的LZ77算法、Huffman编码以及二阶文本建模等方式进行数据压缩,其他压缩算法相比,它有着更高的压塑压缩效率。

使用相应的压缩方法在带宽一定的情况下确实有不错的效果,但是GZIP等主要针对文件压缩效果不错,但是对视频就不行了。这个时候是不是可以使用数据结构中常用的分而治之,大化小再合并的方式呢。

OK,在报文中使用"Transer-Encoding:chunked"表示,代表Body部分数据是分块传输的。另外在Cody中存在一个Content-length字段表示Body的长度,两者不能共存,另外很多时候是流式数据,Body中没有指明Content-length,这个时候一般就是Chunked传输了。

现在可以通过采用分块的方式增强带宽的利用率,那他的编码规则如何呢?

<1> 每一个分块包含长度和数据块
<2> 长度头按照CRLF结束
<3> 数据块在长度快后,且最后CRLF结尾<4>使用长度0表示结束,"0\r\n\r\n"

我们还是看图加深印象:

分块解决了咋们一部分问题,但是有的时候我们想截断发送怎么办呢。在HTTP中提供了使用字段“Accept - Ranges: bytes”,明确告知客户端:“我是支持范围请求的”。那么Range范围是怎样的呢,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请求剩余即可。

现在我们通过MIME-TYPEEncoding-type可以知道body部分的类型,下一步将是对内容进行协商。HTTP中,请求体中使用Accept告诉服务端需要什么类型数据(我能处理哪些类型数据),响应头中使用Content表明发送了什么类型数据,具体如下图所示:

好了,为了各个国家民族顺利友好的沟通和明确的区分。HTTP请求头中使用"type-subtype",注意此时分隔符是"-"。比如EN-GB表示英式英语,ZH-CN表示常用的汉语,那对于客户端而言,它通过Accept-Language来标记自己可以理解的自然语言,对应的服务端使用Content-Language表明实体数据使用的语言类型,如下图所示。

5、Cookie机制

HTTP是无状态、无记忆的,Cookie机制的出现让其有记忆功能,是怎么实现呢?

从上图我们可以知道Cookie是由浏览器负责存储,并不是操作系统负责,我们换个浏览器。打开同样的网页,服务就认不出来了。

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

6、HTTP代理

之前介绍的都是一问一答的情景,但是在大部分的情况下都会存在多台服务器进行通信服务。其中比较常见的就是在请求方与应答方中间增加一个中间代理。

代理作为中间位置,相对请求方为服务端,相当于后端服务端为请求方。代理常见的功能为负载均衡。在负载均衡中需要区分正向代理与反向代理,其中也就会涉及调度算法,比如轮询,一致性哈希等。

代理作为隐藏身份,相当于隐藏了真实的客户端与服务端。

上一篇 下一篇

猜你喜欢

热点阅读