HTTP协议
HTTP协议:超文本传输协议,是一种通信协议,应用层协议,其主要作用就是规定了客户端和服务端请求和应答的数据标准,默认端口:80
HTTP协议
是建立在TCP/IP协议
上的,TCP/IP协议
是用于建立服务端和客户端的连接,连接之后就是两端就按照HTTP协议进行通信,详细流程见浏览器如何显示页面(一)
HTTP协议随着计算机网络和浏览器而诞生,浏览器出现之前,人们是怎样上网的?,不管怎么说,那个时代对于现在的我们,有点难以想象。。。之后,网景发布了Netscape Navigator
浏览器,才慢慢打开了互联网的幕布。如果根据OSI
来划分的话,HTML
属于表示层,而HTTP
属于应用层。HTTP发展至今,经过了HTTP0.9
、HTTP1.0
、HTTP1.1
、HTTP2.0
的时代,虽然2.0很久之前就正式提出标准,大多浏览器也支持了,但是网络支持HTTP2.0
的却很少。
HTTP报文
报文,是网络通信中的基本传输单元,即一次发送的数据块,HTTP报文是一行行纯文本,且是明文显示,即:如果能监听你的网络,那么你发送的所有账号密码都是可以看见的,为了保障数据隐秘性,HTTPS随之而生。
- 请求报文
请求报文分为三部分:
- 请求行
请求行由方法字段
+URL字段
+HTTP协议版本
组成,其中方法字段需大写,其方法如下:方法字段 使用场景 GET 请求获取Request-URI(URI:通用资源标识符,URL是其子集,URI注重的是标识,而URL强调的是位置,可以将URL看成原始的URI),所标识的资源 POST 在Request-URI所标识的资源后附加新的数据;支持HTML表单提交,表单中有用户添入的数据,这些数据会发送到服务器端,由服务器存储至某位置(例如发送处理程序) HEAD 请求Request-URI所标识的资源响应消息报头,HEAD方法可以在响应时不返回消息体。 PUT 与GET相反,请求服务器存储一个资源,并用Request-URI做为其标识;例如发布系统。 DELETE 请求删除URL指向的资源 OPTIONS 请求查询服务器的性能,或者查询与资源相关的选项 TRACE 跟踪请求要经过的防火墙、代理或网关等,主要用于测试或诊断 CONNECT 保留将来使用
请求URL:
一个完整的包括类型、主机名和可选路径名的统一资源引用名,如:http://www.example.com/path/to/file.html
- 请求头部
请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
关键字 | 内容 |
---|---|
User-Agent | 产生请求的浏览器类型 |
Accept | 客户端可识别的响应内容类型列表;星号 “ * ” 用于按范围将类型分组,用 “ / ” 指示可接受全部类型,用“ type/* ”指示可接受 type 类型的所有子类型 |
Accept-Language | 客户端可接受的自然语言 |
Accept-Encoding | 客户端可接受的编码压缩格式 |
Accept-Charset | 可接受的应答的字符集 |
Host | 请求的主机名,允许多个域名同处一个IP 地址,即虚拟主机; |
connection | 连接方式(close 或 keepalive),如果是close的话就需要进行TCP四次挥手关闭连接,如果是keepalive,表明还能继续使用,这是HTTP1.1对1.0的新增,加快了网络传输,默认是keepalive; |
Cookie | 存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie; |
Pragma | Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache- Control:no-cache相同 |
Referer | Referer头域允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化 cache等 |
Range | Range头域Range头域可以请求实体的一个或者多个子范围 |
- 请求数据
请求包体不在 GET 方法中使用,而是在POST 方法中使用。POST 方法适用于需要客户填写表单的场合。与请求包体相关的最常使用的是包体类型 Content-Type 和包体长度 Content-Length;
响应报文分为三部分:
- 状态行
状态码 | 响应状态 |
---|---|
1XX | 这一类型的状态码,代表请求已被接受,需要继续处理。这类响应是临时响应,只包含状态行和某些可选的响应头信息,并以空行结束。 |
2XX | 这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。 |
3XX | 这类状态码代表需要客户端采取进一步的操作才能完成请求。通常,这些状态码用来重定向,后续的请求地址(重定向目标)在本次响应的Location域中指明。 |
4XX | 这类的状态码代表客户端类的错误 |
5XX | 服务器类的错误 |
- 响应头
属性 | 内容 |
---|---|
Date | 消息产生的时间 |
Age | (从最初创建开始)响应持续时间 |
Server | 向客户端标明服务器程序名称和版本 |
ETage | 不透明验证者 |
Location | URL备用的位置 |
Content-Length | 实体的长度 |
Content-Tyep | 实体的媒体类型 |
- 响应实体
实体包含了Web客户端请求的对象。Content-Length标头及Content-Type标头用于计算实体的位置、数据类型和数据长度。当Web服务器接收到Web客户端的请求报文后,对HTTP请求报文进行解析,并将Web客户端的请求的对象取出打包,通过HTTP响应报文将数据传回给Web客户端,如果出现错误则返回包含对应错误的错误代码和错误原因的HTTP响应报文。
HTTP特性
- 1.无状态性
当客户端访问完一次服务器再次访问的时候,服务器是无法知道这个客户端之前是否已经访问过了。优点是不需要先前的信息,能够更快的应答,缺点是每次连接传送的数据量增大。这种做法不利于信息的交互,随后,Cookie和Session就应运而生,至于它俩有什么区别,可以看看COOKIE和SESSION有什么区别?
- HTTP协议是无状态的,也就是每次的HTTP请求,都是独立没有关联的;
2.Session,位于服务端,服务端通过Session会话来跟踪用户的状态,以此确定当前的操作行为都是属于一个用户操作;
3.session 默认被存在在服务器的一个文件里;session运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
4.Cookie,是一段文本数据,位于客户端,用于记录用户的一些信息
- 2.持久连接
在HTTP早期版本中,如HTTP0.9/HTTP1.0
中,每次一个HTTP请求完毕,TCP
连接会断开,下一个HTTP
请求又要重新开启TCP/IP
连接,有较大的请求耗时;
在HTTP1.1
使用持久连接keepalive
,所谓持久连接,就是服务器在发送响应后仍然在一段时间内保持这条连接,允许在同一个连接中存在多次数据请求和响应,即在持久连接情况下,服务器在发送完响应后并不关闭TCP连接,客户端可以通过这个连接继续请求其他对象。
HTTP速度影响因素
- 带宽
网络贷款直接影响数据传输的效率; - 延迟
-
浏览器阻塞
一个浏览器对于同一个域名,同时只能有有限个请求,如果多了后续的请求会被阻塞,常见浏览器阻塞数量:
-
DNS查询
DNS查询是将对域名解析成对应的IP地址,这个过程会先找浏览器缓存,本地缓存,路由器缓存,ISP服务缓存,如果没找到指定IP地址,就查询DNS分布式服务器,这个过程也会阻塞HTTP请求; -
建立连接
TCP连接,HTTP请求是建立在TCP连接成功之后,即使网络、浏览器再快也要进行TCP的三次握手,在高延迟的场景下影响比较明显,慢启动则对文件请求影响较大。
-
HTTP缺点
- 耗时: 传输数据每次都需要建立连接;
- 不安全:HTTP是明文传输的,只要在路由器或者交换机上截取,所有东西(账号密码)都是可见的
- Header内容过大:通常,客户端的请求header变化较小,但是每次都要携带大量的header信息,导致传输成本增大;
- Keep-alive压力过大:持久连接虽然有一点的优点,但同时也会给服务器造成大量的性能压力,特别是传输图片的时候。
HTTP的不同版本
HTTP1.0:1996年,可以传输图片,视频,二进制文件;增加get、post、head命令,丰富了客户端和服务端的交互;
缺点:每个TCP连接都只能发送一次请求,发送完数据后,TCP连接就会断掉;如果还需要请求其他资源,需要再次建立TCP连接;【解决:非标准方案:使用connection方法:keep-alive】
HTTP 1.1:1997年发布,至今仍然是最流行的协议版本;
改进点:1.将connect:keep-alive持久连接作为标准写入,TCP连接默认不关闭;
2.引入管道机制,即在同一个TCP连接中,可以同时发送多个请求,进一步提高了HTTP协议的效率;
3.增加了更多的交互命令;
缺点:
尽管加入管道机制,可以在同一个TCP连接中,多次发送请求,但是服务器仍然按照请求的顺序,执行完一个请求后再处理下一个请求;会造成一个请求阻塞后续请求的情况;
处理办法:1.减少请求次数;同时开多个持久连接;
HTTP2.0:2015年发布,优点:
1.多工:HTTP2.0中,客户端、服务端可以在同一个TCP连接中同时发送多个请求或者响应,但是顺序不用一一对应,避免了对头阻塞的问题;
举例:客户端有A和B两个请求,当服务端发现A请求处理时间过长时,会将已经A请求已经处理好的部分先返回;然后处理B请求,处理完再接着处理A请求;
2. 通过数据流方式发送数据,因为顺序可能不同,需要用数据流ID标记每段数据,其中,客户端向服务端发送的数据流ID为奇数,服务端发送给客户端的数据ID为偶数;【在http1.1中取消数据流方法需要关闭TCP连接,而HTT2.0中可以取消某一次请求,但是TCP连接还保持开启状态】、
3. 只支持二进制协议,方便解析数据,而HTTP1.1中可以使用二进制和文本形式存储头部信息,会比较麻烦;
HTTPS
由于HTTP报文的不安全性,网景在1994年就创建了HTTPS,并用在浏览器中。最初HTTPS是和SSL一起使用,然后演化为TLS。SSL/TLS在OSI模型中都是表示层的协议。 SSL使 用40 位关键字作为RC4流加密算法,这对于商业信息的加密是合适的。
SSL/TLS加密协议
- SSL(Secure Sockets Layer),简称安全套接入层,最初由上世纪90年代由网景公司设计。开启 SSL 会增加内存、CPU、网络带宽的开销,后二者跟你使用的 cipher suite 密切相关,其中参数很多,很难一概而论。开启 SSL 的前提是你的 cert 和 key 必须放在 TCP endpoint,你是否信得过那台设备。
- TLS(Transport Layer Security),简称安全传输层协议,该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面,与具体的应用无关,所以,一般把TLS协议归为传输层安全协议。【对称加密和非对称加密】
SSL/TLS握手的时候使用的是非对称加密,速度相对比较慢,为了减少计算耗时,在握手完成之后,就会切换到对称加密状态。对称加密的秘钥叫做“对话秘钥”(session key),每个session生成一次,通过握手的时候生成的三次随机数完成的。
HTTP缓存
http中具有缓存功能的是浏览器缓存,以及缓存代理服务器。
http缓存的是指:当Web请求抵达缓存时, 如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档。
- 缓存的好处
- 减少了冗余的数据传输,节省了网费。
- 减少了服务器的负担, 大大提高了网站的性能
- 加快了客户端加载网页的速度
-
如何判断缓存新鲜度。
- 浏览器把缓存文件的最后修改时间通过 header ”If-Modified-Since“来告诉Web服务器。
- 浏览器把缓存文件的ETag, 通过header “If-None-Match”, 来告诉Web服务器
方法1:通过最后修改时间判断
- 浏览器向服务端发送请求,首先检查本地是否有缓存,如果有缓存文件,获取文件最后修改时间,并且通过
if-Modified-Since
发送Request到服务器;- 服务器收到请求后,将服务器中文件最后一次修改的时间
Last-Modified
与请求头中的If-modified-Since
比较,如果时间一样,表示缓存时最新的文件,直接返回304,Not Modified给客户端,让客户端直接使用缓存文件;
3.如果服务端和缓存中最后一次时间不一样,则直接发送最新的请求文件给到客户端;
缓存有效:
缓存失效:
使用Fiddler查看博客园,刷新查看,使用了缓存;
方法2:ETag方法判断新鲜度
为什么使用ETag呢? 主要是为了解决Last-Modified 无法解决的一些问题:
- 某些服务器不能精确得到文件的最后修改时间, 这样就无法通过最后修改时间来判断文件是否更新了。
- 某些文件的修改非常频繁,在秒以下的时间内进行修改. Last-Modified只能精确到秒。
- 一些文件的最后修改时间改变了,但是内容并未改变。 我们不希望客户端认为这个文件修改了。
ETag是实体标签(Entity Tag)的缩写, 根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果)
,可以标识资源的状态。 当资源发送改变时,ETag也随之发生变化。
实例, 打开Fiddler, 打开博客园首页。 你可以看到很多图片,或者CSS文件都是用了缓存。 这些都是通过比较ETag的值,来判断文件是否更新了。
与缓存有关的Header
请求头
属性 | 内容 |
---|---|
Cache-Control: max-age=0 | 缓存最大时效,以秒为单位 |
If-Modified-Since: Mon, 19 Nov 2012 08:38:01 GMT | 缓存文件的最后修改时间。 |
If-None-Match: “0693f67a67cc1:0” | 缓存文件的Etag值 |
Cache-Control: no-cache | 不使用缓存 |
Pragma: no-cache | 不使用缓存 |
响应头
属性 | 内容 |
---|---|
Cache-Control: public | 响应被缓存,并且在多用户间共享 |
Cache-Control: private | 响应只能作为私有缓存,不能在用户之间共享 |
Cache-Control:no-cache | 提醒浏览器要从服务器提取文档进行验证 |
Cache-Control:no-store | 绝对禁止缓存(用于机密,敏感文件) |
Cache-Control: max-age=60 | 60秒之后缓存过期(相对时间) |
Date: Mon, 19 Nov 2012 08:39:00 GMT | 当前response发送的时间 |
Expires: Mon, 19 Nov 2012 08:40:01 GMT | 缓存过期的时间(绝对时间) |
Last-Modified: Mon, 19 Nov 2012 08:38:01 GMT | 服务器端文件的最后修改时间 |
ETag: "20b1add7ec1cd1:0" | 服务器端文件的Etag值 |
如果同时存在cache-control和Expires怎么办呢?
浏览器总是优先使用cache-control,如果没有cache-control才考虑Expires
不使用缓存
- 浏览器发送Http request, 给Web 服务器, header中带有Cache-Control: no-cache. 明确告诉Web服务器,客户端不使用缓存。
- Web服务器将把最新的文档发送给浏览器客户端.
直接使用缓存,不去服务器验证
按F5刷新浏览器和在地址栏里输入网址然后回车。 这两个行为是不一样的。
-
按F5刷新浏览器, 浏览器会去Web服务器验证缓存。
-
如果是在地址栏输入网址然后回车,浏览器会"直接使用有效的缓存", 而不会发http request 去服务器验证缓存,这种情况叫做缓存命中,如下图
实例: 比较第一次访问博客园主页和第二次博客园主页 -
启动Fiddler, 用firefox打开博客园主页, 发现有50多个session。
-
按CTRL+X将Fiddler中的所有session删除。 关闭firefox,重新打开一个firefox,打开博客园主页。 发现只有30多个session.
分析: 少了的session是因为firefox直接用了缓存,而没有发http request。
公有缓存和私有缓存
Cache-Control: public 指可以公有缓存, 可以是数千名用户共享的。
Cache-Control: private 指只支持私有缓存, 私有缓存是单个用户专用的。
参考:
TCP/IP、HTTP、HTTPS、HTTP2.0
http协议详解
http请求content-type与响应responseType
HTTP协议&TCP/IP协议
http报文详解
HTTP请求头详解