HTTP(超文本传输协议)
1.为什么叫超文本传输协议
超文本:不止于文本,图片,影视等在Web浏览器里我们都能看到.
传输:双向传输,具有双方交流意义的协议方式,浏览器向某个网址请求资源时,服务端也会返回相应的资源.这种点与点的信息交流方式被称为HTTP
1.1从HTTP需要的url说起
- 利用网址实现协议:处了HTTP请求,存在其他更多的请求,只不过访问Web服务器时常用HTTP,所以很多人在一想到网址时总会想到http,其实还存在HTTPS,FTP, FTPS,SFTP,SMTP,我们重点放在HTTP请求中,以访问互联网的方式了解HTTP的url格式
例如:http://user:[email protected]:80/dir/file.html?uid=1#ch1
下面我们对这段url进行解析:
1.2拿到url浏览器会进行解析
- 浏览器的首要工作便是对URL进行解析,从而生成发送给Web服务器的请求信息
将URL拆分如下:
我们来理解URL以 / 结尾的URL,例如:http:www.example.com/dir/,如果按照目录向下的层次结构,那下一层就应该是文件,这时候没有指明出来时,浏览器会访问默认的文件(比如index.html或者default.html),这个一般根据服务器设置而定.
这里www.example.com把他看作一个域名,由于一个服务器其实可以对应多个域名,这就导致在解析这个域名的时候会产生同样的IP地址,这时就需要明白需要访问的是哪个域名了(对于服务端来说)
- 虚拟主机(Virtual Hosting):假设有两个域名:example1.com 和 example2.com,它们都指向同一个服务器的IP地址。服务器可以配置两个虚拟主机,每个虚拟主机针对一个域名提供不同的网站内容。
- 对于 example1.com,虚拟主机配置会指定该域名的网站根目录、日志文件路径等相关设置。
- 对于 example2.com,另一个虚拟主机配置会指定该域名的不同网站根目录、日志文件路径等相关设置。
- 主机头(Host Header):还是上述那个例子,只需要在主机头里加上需要访问的域名便行
- 反向代理(Reverse Proxy):通过反向代理去实现域名和服务器资源(要访问的资源)的映射关系,通过反向代理服务器转发给相应的后端服务器,常见的反向代理服务器:Nginx;
2.HTTP的基本原理
在生成HTTP请求消息之前,先了解一下HTTP的基本原理比较合适
2.1HTTP1.0是不保存状态的协议
- 状态:这个词其实非常的关键,因为没有绝对静止的物体,虽然进行网络通信的过程中,双方仅仅看起来就像是你来我往了一会,实际上在很多场景下,我们需要去维持那个状态,就比如你进寝室第一次大家都认识你,第二次进去他们就不认识你了.计算机之间并没有人脸识别,所以就需要以一种方式去记录两个计算机之间的通信状态,在早期的HTTP1.0中是并不存在这种机制的,因此对于发送的请求和相应都不会持久化处理.(不写入磁盘).
HTTP1.0重复建立连接会导致什么?
这是一次的HTTP请求下的结果,但现在网页当中,往往以HTML的形式出现,包含着大量的图片,一次就请求所有的数据情况已经不太可能,所以会造成多次请求,这意味着建立多次TCP连接,会增加通信的开销。
2.2告知服务器HTTP的意图
HTTP请求之所以有多种方式,其原因就是在于对于资源的请求客户端采取的不同态度也就是不同需求所决定的。
- GET:获取资源,获取服务器指定的资源.
GET请求获取多次,资源返回的也是一样的(前提是服务器资源没有变更),这也就是幂等
- POST:用来传输实体的主题,虽然GET方法也可也传输实体的主题,但一般不用GET方法,POST的含义是提交,主要目的是为了告知服务器一些信息,用来向服务器传输一些数据,得到响应是次要的.
POST请求因为会提交某些数据,可能会更改服务器上的内容,所以对于服务器来说这是不安全的,也不幂等.
GET与POST:通常来说使用POST请求时,携带的数据都会存放到body中,而GET通常请求参数都放在URL中,所以GET的这种方式其实并不安全,但这并不代表GET不能用请求体去请求,只不过RFC这样规定的罢了.
- PUT:传输文件,就如同FTP协议一样上传文件,要求是在请求报文的主体中包含文件内容,然后保存到请求URL的指定位置,不过这种方法存在安全机制,如果谁都能这样传输文件给服务端,传不好的东西可不好,所以仅仅了解即可.
- HEAD:获得响应的首部,和GET方法类似,但不需要获得响应的主体部分,用于确认这个报文信息是否发生更改等检查资源是否有效的一种请求方式
- DELETE:删除文件,你怎么看待PUT,你就怎么看待DELETE就行,这种方法本身也不安全,一般Web网站都会禁用
- OPTIONS:询问支持的方法,就比如某些网站资源,服务器对资源的请求方式做了限制,而这种方式是询问可以支持的请求方式
- TRACE:明面意思就是跟踪,就是经由了哪些服务器,这个过程发生了什么.(服务器和客户端之间通常存在代理服务器,有一些做转发,有一些做缓存,还有的是分担服务器的访问量)
- CONNECT:要求与代理服务器之间通信建立隧道,使用隧道协议进行TCP通信,这种隧道协议通常来说就是TLS和SSL,进行加密后经网络隧道传输
2.3HTTP1.1的提升
- 上述了解到HTTP1.0上的缺点,HTTP1.1在此采用了长连接的一种持久连接,也就是keep-alive,放入Coonect字段(请求头字段),任意一方如果没提出断开连接,则保持TCP的连接状态,这样就无需多次连接TCP了,因为持久化这种状态是要双方都需要进入,所以这种方式减轻了双方的负担.
- HTTP请求也不需要进行等待,如果要等待上一个请求响应的到达才能进行下一次请求,这种请求方式是得不偿失的,如同TCP传包一样尽量减少去等待,这种设计思想是贯穿全层的,所以管线化技术支持不用等待响应也可也继续发生请求.
状态的记录该如何去解决?
使用Cookie技术
最简单的例子就是很多网站都需要去登录认证,只需要在请求报文中加入Cookie字段,在下一次请求中携带服务端便知道了登陆状态;
客户端在第一次向某个网站发生请求后,对应的服务端便会返回一个携带响应的Cookie存放在客户端,下次客户端便可以将Cookike带上在接下来的访问.
- 而Cookie的名称一般是由服务端决定的,假设一个客户登录了某个网站,保持了登录的状态,服务器会生成一个对应的session_id和Cookie,其中包含一个随机生成的标识符作为值;
Set-Cookie: session_id=abc123def456; Expires=Wed, 01 Jun 2023 12:00:00 GMT; Path=/
在上述示例中,服务器生成了一个名为 "session_id" 的 Cookie,并将值设置为 "abc123def456"。此 Cookie 还包含其他属性:
Expires
:指定了 Cookie 的过期时间,表示在 2023 年 6 月 1 日 12:00:00 GMT 之后,该 Cookie 将过期失效。Path
:指定了 Cookie 的路径,这里设置为 "/",表示该 Cookie 对整个网站的所有路径都有效。
当客户端收到该响应后,会保存这个 Cookie,并在以后的请求中自动将该 Cookie 发送回服务器。
在后续的请求中,客户端的请求头部如下图所示:
GET /example HTTP/1.1
Host: www.example.com
Cookie: session_id=abc123def456
2.4了解HTTP报文的格式
- HTTP报文大致可划分为两个部分:首部和报文主体,无论是请求报文还是响应报文都可以大致这么划分
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<html>
<body>
<h1>Hello, World!</h1>
</body>
</html>
上述片段,这是一个响应报文的样例,前三行是首部,其余几行是报文主题.
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Content-Type: application/json
上述片段,这是一个请求报文的样例,这里没有请求报文,是因为是GET请求,更多是以获取的一种角度去获取资源,根据请求的方式不同,首部和报文主体中,首部是一定出现的,因为里面包含着很多重要信息,因此报文其实是可选的.
2.5根据HTTP状态码得知处理结果
- 如何根据响应的结果判断响应的内容是否发生错误,状态码用来作为衡量一个正常的请求再好不过了
状态码的类别:
类别 | 原因 | |
---|---|---|
1XX | 信息类状态码 | 接受的请求正在处理 |
2XX | 成功类状态码 | 请求正常处理完毕 |
3XX | 重定向状态码 | 需要进行额外的操作完成 |
4XX | 客户端错误状态码 | 服务端无法处理请求 |
5XX | 服务端错误状态码 | 服务端处理发生错误 |
- 第一类中:我基本很少见过,现实生活中应该比较少见
2XX开头,一般是比较喜欢看到的结果,有一些常见的状态码需要我们了解认识
- 200 : OK,这个没什么好说的.
- 204: NO Content,处理成功,但没资源返回,也就是没有响应体,比如PUT,DELETE,OPTIONS,这几类请求方式,即不需要响应什么主体内容,只需要告知服务端完成什么操作时,这一类情景时,就返回204
- 206: 范围请求,只请求某一部分,通常在大文件或者断点传续过程,客户端带有Range的头部的GET请求来获取一部分,例如,如下图所示:
GET /file.txt HTTP/1.1
Host: example.com
Range: bytes=500-999
服务器的响应如下:
HTTP/1.1 206 Partial Content
Content-Type: text/plain
Content-Length: 500
Content-Range: bytes 500-999/2000
...(文件的部分内容)...
紧接着就是3XX开头,一般需要进行下一步的处理后才能获取正确的响应结果
- 301:永久性重定向,资源路径已经更改,被分配了新的URI,也就是告知客户端更换新的URL,如下段所示:
HTTP/1.1 301 Moved Permanently
Location: http://example.com/new-page
- 302:FOUND,临时重定向,希望用户本次使用新的URL进行访问
HTTP/1.1 302 Found
Location: http://example.com/new-page
!!! 需要注意的是在进行重定向后,为了确保不同的浏览器和服务器之间在处理这些状态码时具有不同的差异,所以为了避免不确定性,依旧使用原来的请求方法
- 303: See Other,当客户端收到303响应时,它会自动将请求的URL替换为响应中提供的新URL,并将请求方法修改为GET方法,然后重新发送请求到新的URL。这样客户端会使用GET方法获取重定向后的资源。
HTTP/1.1 303 See Other
Location: http://example.com/new-page
!与302不同的是,303明确的指定了请求方法应使用GET方法
- 304: Not Modified,字面意思是没有被修改的意思,一般用于缓存,因为缓存要求数据一致性,因此客户端在请求头中如果包含了一些字段如If-Modified-Since或If-None-Match字段这种附带条件的字段,服务器就会去判断请求的资源是否发生了改变,之前了解过缓存服务器,客户端便会从缓存那获取资源,缓存其实也是提升网络性能的一种,不光如此,CPU cache缓存,快表, Redis中都具备缓存,可以说其实很多地方都应用到了缓存的这种思想.
HTTP/1.1 304 Not Modified
4XX:一般都指向客户端发生错误
思考一个问题:既然客户端发送的请求存在问题或错误,那么为什么一开始在浏览器解析之后没有表示出来,而需要服务端去发送错误的状态码来告知客户端?
浏览器并不是万能的,既然是对服务端的请求,他唯一能保证的就是有关域名和地址以及协议的合法性,至于服务端资源是否存在,以及某些身份验证的问题,浏览器是并不具备的,而这时候必须要通过服务端通过状态码去得知自己的请求哪里出了问题
- 400:客户端发送的请求存在一些情况之一:请求参数不正确,请求中的数据格式不正确,请求内容过长,请求语法无法被理解
HTTP/1.1 400 Bad Request
Content-Type: text/plain
Bad Request: Invalid parameter value.
- 401:被访问的资源收到了保护,需要认证,简言之就是未授权的(Unauthorized),通常没有携带认证所需要的信息,比如身份令牌,或者过期之类的
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Area"
- 403:Forbidden,访问被拒绝了.
- 404:在服务器中没有找到请求的资源
5XX:一般由于服务器自身的错误导致
- 500:服务器执行某些步骤时,发生错误,或者有些故障
- 503:服务器忙碌,或者停机维护什么的,无法处理请求.
3.生成HTTP请求信息
理解的HTTP的基本知识后,让我们继续了解浏览器的下一步过程,在对URL进行解析之后,浏览器就确定了Web服务器和文件名,之后浏览器会根据严格的规定生成HTTP请求消息
- 首先,是生成请求头,也就是我们的首部,首部的开头,往往就直接说明了客户端要进行怎样的操作,也就是请求方式,因此这个请求方式是如何决定的呢?
根据用户的需求和场景去决定.例如以下场景
- 请求网页内容:当你在浏览器地址栏中输入一个URL并按下回车键时,浏览器会自动发送GET请求,用于获取页面内容。
- 表单提交:当你在网页上填写表单并点击提交按钮时,浏览器会自动发送POST请求,将表单数据发送到服务器。
- 点击链接:当你点击一个链接时,浏览器会根据链接的属性和上下文来确定使用适当的请求方法。通常情况下,链接会使用GET请求来获取链接指向的资源。
- JavaScript发起请求:通过JavaScript代码,你可以使用XMLHttpRequest对象或fetch API来手动发送HTTP请求。在这种情况下,你可以明确指定使用哪种请求方法(如GET、POST、PUT等)。
至此,根据不同的实际场景,浏览器就会生成对应的请求报文.至于HTTP请求头或者消息头中的一些字段,实在过多了,最好慢慢理解,也不必全部明白
4.HTTP1.1的优化
4.11缓存
前文说过对于HTTP1.1他引入了持久连接和管线化技术,来尽可能的提升网络的利用率,在此之前,根据响应码(304),我们也理解到一种新的技术叫缓存,同样由于缓存技术的实现,需要相应的字段去控制和关联缓存技术,这里我们后面再将,先理解缓存应实现在何种位置。
实现方式:缓存服务器,本地缓存。
- 缓存服务器:缓存服务器其实就是资源的一种拷贝,减少对原服务器的访问并发量,尽可能减少负荷,一种负载均衡的思想,可以在客户端和服务端之间起到中转的作用,就像代理一样,所以缓存服务器例如(Nginx)。
- 本地缓存,资源化写入本地内存,这里是可以写入磁盘也可也写入主存的,具体缓存是否写入内存还是磁盘,以及缓存的存储位置和策略,取决于浏览器或应用程序的实现和配置
另一种本地缓存:
其中有很多和缓存相关的字段。我们在HTTP首部再做说明。
4.12压缩
其实压缩也很好理解,压缩不光在我们的计算机中压缩软件和很多压缩手段中常见,网络中对数据也可以进行压缩,因为数据的本质就是二进制,而这些二进制用一定规律的编码格式进行编码成了我们人类的语言。先来了解一下计算机网络中常见的压缩类型
- gzip(GNU zip)
- compress(UNIX 系统的标准压缩)
- deflate(zlib)
- identity(不进行编码)
现在我们再来真正去了解一下压缩这一机制的真正设计思想。
假设现在有着这么一个字符串AAAABBBCDDEEEF这14个字符的文件,进行压缩,我们可以以另外一种形式去表现他,就是A4B3CD2E3F,这样一来字符的长度就变为10,压缩了4个字节,这种压缩机制就是RLE压缩算法,但对于不同的数据其实会采用不同的算法,并不会出现如此具有规律的字符,例如:我们学过的哈夫曼算法其实就是一种压缩算法的手段,根据二进制文件中某些字符的出现频率去进行压缩.
- 无损压缩(可逆压缩)
什么是无损压缩,就是压缩前的源数据与压缩后还原的还原数据没有区别,就被称为无损压缩,这种压缩方式可逆,是为了确保数据的可靠性,例如EXE文件,就不允许有任何一个字符的错误.
- 有损压缩(不可逆压缩)
有损压缩就很简单了,压缩前源数据与压缩后还原的数据不一致,或者说比较接近,这种压缩方式常常被用来处理图像,音频等数据,因为人眼对颜色其实并不敏感,反而对亮度很敏感,而我们的三原色RGB,即使最后图像有一些颜色过于模糊,人眼可能不能清楚地看出,但也能知道大体是什么颜色即可.
4.13分块传输
在HTTP通信过程中,浏览器的页面其实是逐步出现的,而不是一个整体出现,这意味着其实进行了数据的分割,毕竟传的都是大文件,自己协议的工作要靠自己完成,因此在进行分块传输的时候,每一个块都需要添加首部,当作一个独立的数据包进行传递
5.HTTP首部的字段
我先从有关缓存的字段说起,根据通用/请求或者响应,来划分,个人觉得不好理解和记忆
5.1缓存有关的首部字段
- Cache-Control: 用于控制缓存的行为。它可以指定缓存的存储策略、是否进行验证等。常见的取值包括:
- public:表示响应可以被任何缓存(包括公共缓存和私有缓存)缓存。
- private:表示响应只能被私有缓存缓存,不能被共享缓存存储。
- no-cache:表示缓存必须在使用之前进行验证,以确保它仍然是最新的。
- no-store:表示缓存不应存储任何关于该响应的副本。
- Expires: 指定响应的过期时间,即缓存应该在此时间之后丢弃响应。它是一个表示具体日期/时间的字符串。
- Expires: Wed, 21 Jul 2023 10:30:00 GMT
- Last-Modified: 指定资源的最后修改时间。客户端可以将此值与后续请求中的If-Modified-Since首部进行比较,以确定资源是否已经修改。
- Last-Modified: Thu, 20 Jul 2023 15:30:00 GMT
- ETag: 表示资源的实体标签,通常是资源内容的哈希值。客户端可以将此值与后续请求中的If-None-Match首部进行比较,以确定资源是否已经修改。
- ETag: "abc123"
- If-Modified-Since: 客户端发送此首部字段,用于条件性获取资源。如果资源在指定的日期之后没有被修改,服务器将返回304 Not Modified响应,而不是完整的资源内容。
- If-Modified-Since: Thu, 20 Jul 2023 15:30:00 GMT
- If-None-Match: 客户端发送此首部字段,用于条件性获取资源。它包含之前获取到的资源的ETag值,如果服务器上的资源ETag与该值匹配,服务器将返回304 Not Modified响应
- If-None-Match: "abc123"
5.2压缩有关的首部字段
- Accept-Encoding:在请求头中使用,用于告知服务器客户端支持的压缩算法。服务器可以根据该字段选择适当的压缩算法对响应进行压缩。示例:
- Accept-Encoding: gzip, deflate
- Content-Encoding:在响应头中使用,用于指示服务器对响应内容进行了压缩的算法。客户端收到响应后可以根据该字段解压缩内容。示例:
- Content-Encoding: gzip
5.3分块有关的首部字段
- Transfer-Encoding:在响应头中使用,用于指示响应的传输编码方式。常见的传输编码方式之一是"chunked",表示响应的实体主体使用分块编码方式传输。示例:
- Transfer-Encoding: chunked
5.4一些常见的重要字段
- Content-Type:指示请求或响应中实体主体的媒体类型。示例:
- Content-Type: application/json
- Content-Type: text/html; charset=utf-8
- Content-Length:指示实体主体的长度(以字节为单位)。示例:
- Content-Length: 1024
- Date:指示消息的创建日期和时间。示例:
- Date: Fri, 21 May 2023 10:30:00 GMT
- Host:指示请求的目标服务器的主机名和端口号。示例:
- Host: www.example.com
- User-Agent:指示发起请求的用户代理(如浏览器、应用程序等)。示例:
- User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
- Location:用于重定向响应中,指示新的资源位置。示例:
- Location: https://www.example.com/new-location
- Accept:指示客户端能够处理的媒体类型。示例:
- Accept: application/json
- Accept: text/html
- Server:指示响应中的服务器软件信息。示例:
- Server: Apache/2.4.41 (Unix)