一、请求报文和响应报文的结构
请求报文和响应报文都是由以下4部分组成:
1.请求行/响应行
2.请求头/响应头
3.空行
4.消息主体(请求体/响应体)
请求报文结构:
请求行
格式为:Method Request-URI HTTP-Version 结尾符
结尾符一般用\r\n
根据HTTP标准,HTTP请求可以使用多种请求方法。
GET:请求指定的页面信息,并返回实体主体。
HEAD:类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求主体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
PUT:从客户端向服务器传送的数据取代指定的文档的内容。
DELETE:请求服务器删除指定的页面
CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS:允许客户端查看服务器的性能。
TRACE:回显服务器收到的请求,主要用于测试或诊断。
请求头
通用头:
既可以出现在请求报头,也可以出现在响应报头中
Date:表示消息产生的日期和时间
Connection:允许发送指定连接的选项,例如指定连接是连续的,或者指定“close”选项,通知服务器,在响应完成后,关闭连接
Cache-Control:用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的缓存指令不会影响另一个消息处理的缓存机制)
请求头:
请求报头通知服务器关于客户端请求的信息,典型的请求头有:
Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机
User-Agent:发送请求的浏览器类型、操作系统等信息
Accept:客户端可识别的内容类型列表,用于指定客户端接收那些类型的信息 Accept-Encoding:客户端可识别的数据编码
Accept-Language:表示浏览器所支持的语言类型
Connection:允许客户端和服务器指定与请求/响应连接有关的选项,例如这是为Keep-Alive则表示保持连接。
Transfer-Encoding:告知接收端为了保证报文的可靠传输,对报文采用了什么编码方式。
实体头:
实体报头用来定义被传送资源的信息,既可以用于请求也可用于响应。请求和响应消息都可以传送一个实体,常见的实体报头为:
Content-Type:发送给接收者的实体正文的媒体类型。HTTP content-type 对照表
Content-Lenght:实体正文的长度
Content-Language:描述资源所用的自然语言,没有设置则该选项则认为实体内容将提供给所有的语言阅读
Content-Encoding:实体报头被用作媒体类型的修饰符,它的值指示了已经被应用到实体正文的附加内容的编码,因而要获得Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制。
Last-Modified:实体报头用于指示资源的最后修改日期和时间
Expires:实体报头给出响应过期的日期和时间
响应报文结构:
响应行
响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK
HTTP状态码:
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码Status Code的响应头(server header)用以响应浏览器的请求。
下面是常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,HTTP状态码共分为5种类型:
①1** 信息,服务器收到请求,需要请求者继续执行操作;
②2** 成功,操作被成功接收并处理;
③3** 重定向,需要进一步的操作以完成请求;
④4** 客户端错误,请求包含语法错误或无法完成请求;
⑤5** 服务器错误,服务器在处理请求的过程中发生了错误。
响应头
响应头:
用于服务器传递自身信息的响应,常见的响应报头:
Allow:服务器支持哪些请求方法(如GET、POST等)。
Location:用于重定向接受者到一个新的位置,常用在更换域名的时候
Server:包含可服务器用来处理请求的系统信息,与User-Agent请求报头是相对应的
Transfer-Encoding:告诉浏览器数据的传送格式。
Refresh:告诉浏览器隔多久刷新一次,以秒计。
Set-Cookie:设置和页面关联的Cookie。
Access-Control-Allow-Origin:指定哪些站点可以参与跨站资源共享。
Age:对象在代理缓存中暂存的秒数。
二、输入URL后发生了什么
1、URL解析
地址解析:首先判断你输入的是一个合法的URL还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作。
HSTS:由于安全隐患,会使用HSTS强制客户端使用HTTPS访问页面。
其他操作:浏览器还会进行一些额外的操作,比如安全检查,访问限制
检查缓存:
2、DNS查询
浏览器缓存:浏览器会先检查是否在缓存中,没有则调用系统库函数进行查询。
操作系统缓存:操作系统也有自己的 DNS缓存,但在这之前,会向检查域名是否存在本地的 Hosts 文件里,没有则向 DNS 服务器发送查询请求。
路由器缓存:路由器也有自己的缓存。
ISP DNS 缓存:ISP DNS 就是在客户端电脑上设置的首选 DNS 服务器,它们在大多数情况下都会有缓存。
根域名服务器查询:在前面所有步骤没有缓存的情况下,本地 DNS 服务器会将请求转发到互联网上的根域,下面这个图很好的诠释了整个流程
3、TCP 连接
TCP/IP 分为四层,在发送数据时,每层都要对数据进行封装:
应用层:发送 HTTP 请求:
在前面的步骤我们已经得到服务器的 IP 地址,浏览器会开始构造一个 HTTP 报文,其中包括:
请求报头(Request Header):请求方法、目标地址、遵循的协议等等
请求主体(其他参数)
传输层:TCP 传输报文
传输层会发起一条到达服务器的 TCP 连接,为了方便传输,会对数据进行分割(以报文段为单位),并标记编号,方便服务器接受时能够准确地还原报文信息。
在建立连接前,会先进行 TCP 三次握手。
4、服务器处理请求
5、浏览器接受响应
浏览器接收到来自服务器的响应资源后,会对资源进行分析。
首先查看 Response header,根据不同状态码做不同的事(比如上面提到的重定向)。
如果响应资源进行了压缩(比如 gzip),还需要进行解压。
然后,对响应资源做缓存。
接下来,根据响应资源里的 MIME[3] 类型去解析响应内容(比如 HTML、Image各有不同的解析方式)。
6、渲染页面
不同的浏览器内核,渲染过程也不完全相同,但大致流程都差不多。
HTML 解析
首先要知道浏览器解析是从上往下一行一行地解析的。
解析的过程可以分为四个步骤:
① 解码(encoding)传输回来的其实都是一些二进制字节数据,浏览器需要根据文件指定编码(例如UTF-8)转换成字符串,也就是HTML 代码。
② 预解析(pre-parsing)预解析做的事情是提前加载资源,减少处理时间,它会识别一些会请求资源的属性,比如img
标签的src
属性,并将这个请求加到请求队列中。
③ 符号化(Tokenization)符号化是词法分析的过程,将输入解析成符号,HTML 符号包括,开始标签、结束标签、属性名和属性值。
④ 构建树(tree construction)在上一步符号化中,解析器获得这些标记,然后以合适的方法创建DOM
对象并把这些符号插入到DOM
对象中。
三、get和post的区别
Http的存在是为了客户机和服务器之间的通信。客户机“请求”,服务器“应答”。
名称 | 作用 |
Head | 与get相同,但只返回HTTP报头,不返回文档主体 |
put | 上传指定的URI表示 |
delete | 删除指定资源 |
options | 返回服务器支持的http方法 |
connect | 把请求连接转换到透明的TCP/IP通道 |
其中,put、 delete、post、get对应着对请求进行“增删改查”。今天我们只来说说get和post
get方法最常用,其是从指定的资源请求数据。
其形式为:URL?传输数据;参数&参数。
传输规则为:
数据 | 发送形式 |
英文字母/数字 | 原样 |
空格 | + |
中文/其它字符 | 用Base64加密 |
例子如下:
login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD
post是向指定的资源提交要被处理的数据,比如完成表单数据的提交,将数据提交给服务器处理。
例子如下:
POST/test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
比较项 | get | post |
作用 | 获取数据 | 提交数据 |
是否有数量限制 | 是 | 否 |
传参方式(一般) | url | request body |
限制长度因素 | 浏览器或服务器配置 | 服务器配置和内存大小 |
是否改变服务器的状态 | 否 | 是 |
是否幂等 | 是 | 否 |
提供的资源 | 参数 | 表单 |
根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的。
1、所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。
2、幂等的意味着对同一URL的多个请求应该返回同样的结果。幂等(idempotent、idempotence)是一个数学或计算机学概念,常见于抽象代数中。
幂等有以下几种定义:
对于单目运算,如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的,那么我们就称该运算是幂等的。 比如绝对值运算就是一个例子,在实数集中,有abs(a) = abs(abs(a)) 。
3、post请求的过程,会先将请求头发送给服务器进行确认,然后才真正发送数据;而get请求的过程,会在连接建立后会将请求头和请求数据一起发送。
post请求的过程
1、浏览器请求tcp连接(第一次握手)
2、服务器答应进行tcp连接(第二次握手)
3、浏览器确认,并发送post请求头(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
4、服务器返回100 continue响应
5、浏览器开始发送数据
6、服务器返回200 ok响应
get请求的过程
1、浏览器请求tcp连接(第一次握手)
2、服务器答应进行tcp连接(第二次握手)
3、浏览器确认,并发送get请求头和数据(第三次握手,这个报文比较小,所以http会在此时进行第一次数据发送)
4、服务器返回200 ok响应
get会将数据缓存起来
使用ajax采用get方式请求静态数据(比如html页面,图片),如果检测到get请求的是静态资源,则会缓存。
post不能进行管道化传输
- http在的一次会话需要先建立tcp连接然后才能通信,如果每次连接都只进行一次http会话,那这个连接过程占的比例太大了
-
于是出现了持久连接:在http/1.0+中是connection首部中添加keep-alive值,在http/1.1中是在connection首部中添加persistent值,当然两者不仅仅是命名上的差别,http/1.1中,持久连接是默认的,除非显示在connection中添加close,否则持久连接不会关闭,而http/1.0+中则恰好相反,除非显示在connection首部中添加keep-alive,否则在接收数据包后连接就断开了。
-
出现了持久连接还不够,在http/1.1中,还有一种称为管道通信的方式进行速度优化:把需要发送到服务器上的所有请求放到输出队列中,在第一个请求发送出去后,不等到收到服务器的应答,第二个请求紧接着就发送出去,但是这样的方式有一个问题:不安全,如果一个管道中有10个连接,在发送出9个后,突然服务器告诉你,连接关闭了,此时客户端即使收到了前9个请求的答复,也会将这9个请求的内容清空,也就是说,白忙活了……此时,客户端的这9个请求需要重新发送。这对于幂等请求还好(比如get,多发送几次都没关系,每次都是相同的结果),如果是post这样的非幂等请求(比如支付的时候,多发送几次就惨了),肯定是行不通的。所以,post请求不能通过管道的方式进行通信