HTTP
- HTTP (Hyper Text Transfer Protocol), 超文本传输协议,是互联网中应用最广泛的应用层协议之一, 设计 HTTP 最初的目的是:提供一种发布和接收 HTML 页面的方法,由 URI(注意不是 URL) 来标识具体的资源,后来用 HTTP 来传递的数据格式不仅仅是 HTML,应用非常广泛
- HTML(Hyper Text Markup Language), 超文本标记语言,因为其可以用一段文本来标识超出文本之外的东西,比如超链接,图片等。用来编写网页。
版本
- 1991 年, HTTP/0.9版本, 只支持 GET 请求方法获取文本数据(比如 HTML 文档),且不支持请求头,响应头等,无法向服务器传递太多信息
- 1996 年, HTTP/1.0 版本, 支持 POST、HEAD 等请求方法,支持请求头、响应头等,支持更多中数据类型(不再局限于文本数据),浏览器的每次请求都需要与服务器建立一个 TCP 连接,请求处理完成后立即断开 TCP 连接
- 1997 年,HTTP/1.1 版本,最经典,使用最广泛的版本,支持 PUT 、DELETE 等请求方法,采用持久连接(Connection: keep-alive),多个请求可以共用一个 TCP 连接
- 2015 年, HTTP/2.0
- 2018 年, HTTP/3.0 草稿阶段
标准
- HTTP 的标准由万维网协会(W3C),互联网工程任务组(IETF)协调制定,最终发布了一系列的 RFC
- RFC (Request For Comments 可以译为请求意见稿)
- HTTP/1.1 最早是在 1997 年的 RFC_2068 中记录的,该规范在 1999 年的 RFC_2616 中已作废,2014 年又由 RFC_7230 系列的 RFC 取代
- HTTP/2 标准于 2015 年 5 月以 RFC_7540 正式发表,取代 HTTP/1.1 成为 HTTP 的实现标准
- 中国 的 RFC: 1996 年 3 月,清华大学提交的适应不同国家和地区中文编码的汉字统一传输标准被 IETF 通过为 RFC_1922,成为中国大陆第一个被认可的 RFC 文件的提交协议
报文格式
ABNF
- ABNF(Augmented BNF): 是 BNF(Backus-Naur Form, 巴克斯-瑙尔范式)的修改版、增强版。在 RFC_5234 中标明,ABNF 用作 Internet 中通信协议的定义语言,ABNF 是最严谨的 HTTP 报文格式描述形式,脱离 ABNF 谈论 HTTP 报文格式,往往都是片面的、不严谨的
- 关于 HTTP 报文格式的定义
- RFC_2616_4.HTTP Message(旧)
- RFC_7230_3.Message Format(新)
- 核心规则
报文格式整体
报文格式-request-line、status-line
报文格式 header-filed、message-body
URL 的编码
- URL 中一旦出现了一些特殊字符(比如中文、空格),需要进行编码, 在浏览器地址栏输入 URL 时,采用的是 UTF-8 进行编码
HTPP 的请求方法
- RFC_7231 中的section 4 的 Request methods 中描述了 8 种 请求方法,包括: GET、HEAD、POST、PUT、DELETE、CONNECT、OPTIONS、TRACE
- RFC_5789 中的 section 2的 Patch method 中描述了 PATH 方法
GET
- 常用于读取的操作,请求参数直接拼接在 URL 的后面(浏览器/服务器对 URL 是有长度限制的,不同浏览器/服务器的限制不一样), 由于长度的限制导致 GET 请求不能发送太多的数据
POST
- 常用于添加、修改、删除的操作,请求参数既可以放到URL 中也可以放大请求体中(没有大小限制),通常我们放在请求体中
HEAD
- 请求得到与 GET 请求相同的响应,但是没有响应体。使用场景: 在下载一个大文件之前,先获取其大小,再决定是否要下载,以此可以节约带宽资源
OPTIONS
- 可以用于获取目标资源所支持的通信选项,比如服务器支持的请求方法
PUT
- 用于对已存在的资源进行整体覆盖,比如新上传的文件覆盖旧的
PATCH
- 用于对资源进行部分修改(资源不存在,会创建新的资源)
DELETE
- 用于删除指定的资源
TRACE
- 请求服务器回显其收到请求信息,主要用于 HTTP 请求的测试或诊断
CONNECT
- 可以开启一个客户端与所请求资源之间的双向沟通的通道,它可以用来创建隧道(tunnel),可以用来访问采用了 SSL 协议的站点
头部字段(Header Field)
- 头部字段可以分为 4 种类型
- 请求字段(Request Header Fields): 有关要获取的资源或客户端本身信息的消息头
- 响应头字段(Response Header Fields): 有关响应的补充信息,比如服务器本身(名称和版本)的消息头
- 实体头字段(Entity Header Fields):有关实体主题的更多信息,比如主体长度(Content-Length) 或其 MIME 类型
- 通用头字段(General Header Fields): 同时适用于请求和响应消息,但与消息主体无关的消息头
请求头字段
Snip20210828_10.png- Accept-Charset 中 q = 0.7 中的 0.7 表示的是权重值,值越大表示越倾向于接收该类型的数据, 默认是 1.0, Accept-Charset中的内容是以逗号分隔的,不是以;分隔的
- Range 可以用于多线程下载
响应头字段
同源策略
- 浏览器有个同源策略(Sam-Origin Policy)
- 它规定了: 默认情况下,AJAX请求只能发送给同源的 URL
- 同源是指 3 个相同: 协议、域名(IP)、端口 ,有一个不一样就不是同源
- img、script、link、iframe、video、audio 等标签不受同源策略的约束
跨域资源共享
- 解决 AJAX 跨域请求的常用方法
- CORS(Cross-Origin Resource Sharing),跨域资源共享
- CROS 的实现需要客户端和服务器同时支持
- 客户端所有的浏览器支持(IE 至少是 IE10 版本)
- 服务器需要返回相应的响应头(比如 Access-Control-Allow-Origin), 告知浏览器这是一个允许跨域访问的请求
会话跟踪
- HTTP 是一种"无状态"(stateless)的协议, 每次客户端访问网页时,客户端都会打开与 web 服务器的单独连接,并且服务器不会自动保留之前客户端请求的任何记录,所以服务器无法识别多个请求是否来自同一客户端(比如浏览器)
- 在很多应用场景中,都有以下需求
- 服务器能够识别出多个请求是否来自同一个客户端
- 在来自同一个客户端的多个请求之间共享数据
- 以上需求可以使用会话跟踪技术来完成,在 java 中,实现会话跟踪常用方案是 Cookie 和 Session
Cookie
- Cookie 是直接存储在浏览器本地的一小串数据,
- 使用 document.cookie 访问 Cookie
- 修改 Cookie 时,只会修改其中提到的 Cookie
- name=value 必须被编码(encodeURIComponent)
- 一个 Cookie 最大为 4kb,每个网站最多有 20+个左右的 Cookie(具体取决于浏览器)
- Windows 中的 Chrome 浏览器的 Cookie 存放位置是:
C:\User\用户名\APPData\Local\Google\Chrom\User Data\Default\Cookies
, 使用 SQLite 数据库进行存储
Cookie 的有效期
- 如果没有设置 Cookie 的过期时间,则当浏览器关闭时,Cookie 就失效了
- expires: 必须完全采用 GMT 时区的格式,可以使用 date.toUTCString 来获取,例如: expires=True,19 Jan 2038 03:14:07 GMT
- max-age:过期时间距离当前时间的秒数,例如:max-age=60
Cookie 的作用域
- domain 和 path 标识定义了 Cookie 的作用域,即 Cookie 应该发送给哪些 URL
- domain 标识指定了哪些主机可以接受 Cookie, 如果不指定,默认为当前文档的主机(不包含子域名);如果指定了 domain,则一般包含子域名 例如:如果设置 domain=baidu.com,则 Cookie 也包含子域名中(如 bbs.baidu.com)
- path标识指定了主机下的哪些路径可以接受 Cookie,子路径也会被匹配,例如:设置 path=/docs,则以下地址都会匹配,
/docs
、/docs/one/
、docs/one/img
服务器设置 Cookie
- Cookie 通常是由 Web 服务器使用响应头 Set-Cookie 设置的
- 关于 max-age
- 在 JavaScript 中:如果设置为 0 或者为负数,会立即删除 Cookie
- 在 Java 中:如果设置为 0,是立即删除 Cookie;如果设置为负数,按默认情况处理
getSession 内部原理
- 检查客户端是否有发送一个叫做 JSESSIONID 的 Cookie,如果没有,创建一个新的 Session 对象,并且这个 Session 对象会有一个 id,这个 Session 对象会保留在服务器的内存中,在响应的时候,会添加一个 Cookie(JESSIONID=Session 对象的 id)给客户端;如果有,返回 id 为 JESSIONID 的 Session 对象
JESSIONID
- 默认情况下,当用户关闭浏览器时,Cookie 中存储的 JESSIONID 就会销毁
- 可以通过以下代码延长 JSESSIONID 在客户端的寿命
Cookie cookie = new Cookie("JESSIONID",request.getSession().getId());
cookie.setMaxAge(3600);
response.addCookie(cookie);
Session 的有效期
- Session 的有效期默认是 30 分钟
- 可以在 web.xml 中配置失效时间(单位是分钟)
<session-config>
<session-timeout>30</session-timeout>
</session-config>
Cookie / Session 总结
Cookie | Session |
---|---|
数据存储在浏览器客户端 | 数据存储在服务器 |
数据有大小和数量的限制 | 数据没有大小和数量的限制 |
适合存储一些小型、不敏感的数据 | 可以存储大型、敏感的数据(比如用户信息) |
默认情况下,关闭浏览器后就会销毁 | 默认情况下,未使用 30分钟后就销毁 |
状态码
- 在 RFC_2616_10.Status Code Definitions 规范中定义: 状态码指示 HTTP 请求是否已成功完成
- 状态码可以分为 5 类
- 信息响应: 100~199
- 成功响应: 200~299
- 重定向: 300~399
- 客户端错误: 400~499
- 服务器错误: 500~599
常见状态码
- 100: Continue ,请求的初始部分已经被服务器收到,并且没有被服务器拒绝。客户端应该继续发送剩余的请求,如果请求已经完成,就忽略这个响应。允许客户端发送带请求体的请求前,判断服务器是否愿意接收请求(服务器通过请求头判断)。 在某些情况下,如果服务器在不看请求体就拒绝请求时,客户端就发送请求体是不恰当或低效的
- 200: ok ,请求成功
- 302: Found, 请求的资源被暂时的移动到了由 Location 头部指定的 URL 上
- 304: Not Modified, 说明无需再次传递请求的内容,也就是说可以使用缓存的内容
- 400 Bad Request, 由于语法无效,服务器无法理解该请求
- 401 Unauthorized,缺乏目标资源要求的身份验证凭证
- 403 Forbidden, 服务器端有能力处理该请求,但是拒绝授权访问
- 404 Not Found, 服务端无法找到所请求的资源
- 405 Method Not Allowed, 服务器禁止了使用当前 HTTP 方法的请求
- 406 Not Acceptable, 服务端无法提供与 Accept-Charset 以及 Accept-Language 指定的值相匹配的响应
- 408 Request Timeout, 服务器想要将没有在使用的连接关闭, 一些服务器会在空闲连接上发送此信息,即便是在客户端没有发送任何请求的情况下
- 500 Internal Server Error, 所请求的服务器遇到意外的情况并且阻止其执行请求
- 501 Not Implemented, 请求的方法不被服务器支持,因此无法被处理, 服务器必须支持的方法(即不会返回整个状态码的方法)只有 GET 和 HEAD
- 502 Bad Gateway, 作为网关或代理角色的服务器,从上游服务器(如 tomcat)中接收到的响应是无效的
- 503 Service Unavailable, 服务器尚未处于可以接受请求的状态,通常造成这种情况的原因是由于服务器停机维护或者已超载
代理服务器
- 特点:
1、本身不产生内容
2、处于中间位置转发上下游的请求和响应。面向下游的客户端,它是服务器;面向上游的服务器,它是客户端
正向代理、反向代理
- 正向代理:代理的对象时客户端
-
反向代理:代理的对象时服务器
正向代理
- 隐藏客户端身份
- 绕过防护墙(突破访问限制)
- Internet 访问控制
- 数据过滤
... - 一些免费的正向代理
- https://ip.jiangxianli.com
- https://www.kuaidaili.com/free/inha
反向代理
- 隐藏服务器身份
- 安全防护
- 负载均衡
抓包工具的原理
- Fiddler、Charles 等抓包工具的原理: 在客户端启动了正向代理服务
- 需要注意的是Wireshark的原理是通过底层驱动,拦截网卡上流过的数据
CDN
- CND(Content Delivery Network 或 Content Distribution Network): 内容分发网络
- 利用最靠近每位用户的服务器,更快更可靠地将音乐、图片、视频等资源文件(一般是静态资源)传递给用户
- CDN 运营商在全国、乃至全球的各个大枢纽城市都建立了机房,部署了大量拥有高存储带宽的节点,构建了一个跨运营商、跨地域的专用网络
- 内容所有者向 CDN 运营商支付费用,CND 将其内容交付给最终用户
- 使用 CND 之前: 当我们访问一个网站,首先是访问 DNS,DNS 将域名对应的 IP 返回给你,然后你再访问对应 ip 的网站服务器
-
使用 CDN 之后: 当访问一个域名的时候,先去 DNS 服务器取域名,当 DNS 发现这个域名对应的是 CDN 资源,这个时候会去 CDN 的 DNS 服务器要对应的 IP,然后 CDN 的 DNS 服务器会告诉你个 IP 地址,拿到这个 IP 地址后,再去访问CDN 的负载均衡系统,负载均衡系统会返回另外一个 IP 给你,这个 IP 就是离你最近的 CDN 缓存服务器,然后你再根据 IP去请求CDN 缓存服务器
HTTP/HTPPS(1.1 版本) 协议的不足
- HTPP1.1 版本虽然建立的链接可以复用,但同一时间,一个连接只能对应一个请求,也就是说如果有多个请求的话,请求需要排队。针对同一域名,大多数浏览器允许同时最多 6 个并发连接。
- 只允许客户端主动发起请求,一个请求只能对应一个响应
- 同一个会话的多次请求中,头信息会被重复传输,通常会给每个传输增加 500~800 字节的开销,如果使用 Cookie,增加的开销有时会达到上千字节
SPDY
- SPDY(speedy 的缩写),是基于 TCP 的应用层协议,它强制要求使用 SSL/TLS。 2009 年 Google宣布将 SPDY 作为提高网络速度的内部项目
- SPDY 与 HTTP 的关系
- SPDY 并不用于取代 HTTP,它只是修改了 HTTP 请求与响应的传输方式
- 只需增加一个 SPDY 层,现有的所有服务端应用均不用做任何修改
- SPDY 是 HTTP/2 的前身,2015 年google宣布移除对 SPDY 的支持,拥抱 HTTP/2
HTTP/2
- HTTP/2 于 2015 年 5 月以RFC_7540 正式发表,根据 W3Techs 的数据,截至 2019 年 6 月全球有 36.5%的网站支持了 HTTP/2
- HTTP/1.1 和 HTTP/2 速度对比可以看
- HTTP/2 在底层传输做了很多的改进和优化,但在语意上完全与 HTTP/1.1 兼容, 比如请求方法(如 GET、POST)、Status Code、各种 Headers 等都没有改变,因此想要升级 HTTP/2 ,开发者不需要修改任何代码,只需要升级服务器配置,升级浏览器。
- 应用层的东西开发者都可以改,但是 TCP 层的东西就需要操作系统修改了
HTTP/2 特性
二级制格式
- HTTP/2 采用二级制格式传输数据,而非 HTTP/1.1的文本格式
- 二进制格式在协议的解析和优化扩展上带来更多的优势和可能
一些基本概念
- 数据流: 已建立的连接内的双向字节流,可以承载一条或多条消息,所有通信都在一个 TCP 连接上完成,此连接可以承载任意数量的双向数据流
- 消息: 与逻辑 HTPP 请求或响应消息对应,由一系列帧组成
- 帧: HTTP/2 通信的最小单位,每个帧都包含帧头(会标识出当前帧所属的数据流),来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装
多路复用
- 客户端和服务端可以将 HTTP 消息分解为互不依赖的帧,然后交错发送,最后再在另一端把它们重新组装起来
- 并行交错地发送多个请求,请求之间互不影响
- 并行交错的发送多个响应,响应之间互不干扰
- 使用一个连接并行发送多个请求和响应
- 不必再为绕过 HTTP/1.1 的限制而做很多工作,比如:image Sprites、合并 CSS\JS、内嵌 CSS\JS\Base64 图片、域名分片等。
- image Sprites(也叫做 CSS Sprites),将多张小图合并成一张大图,最后通过 CSS 结合小图的位置,尺寸进行精准定位
优先级
- HTTP/2 标准允许每个数据流都有一个关联的权重和依赖关系,可以向每个数据流分配一个介于 1 至 256 之间的整数,每个数据流与其他数据流之间可以存在显式依赖关系
- 客户端可以构建和传递"优先级树",表明它倾向于如何接收响应
- 服务器可以使用此信息通过控制 CPU、内存和其他资源的分配设定数据流处理的优先级。在资源数据可用之后,确保将高优先级响应以最优方式传输至客户端
- 应尽可能先给父数据流分配资源
- 同级数据流(共享相同父项)应按其权重比例分配资源。
- A、B 依赖于隐式"根数据流",A获得的资源比例是 12/16,B 获得的资源比例是 4/16
- D 依赖于根数据流,C 依赖于D,D 应先于 C 获得完整资源分配
- D 应先于C获得完整资源分配,C 应先于 A 和 B 获得完整资源分配,B 获得的资源是 A 所获资源的 1/3
- D 应先于 E 和 C 获得完整资源分配,E 和 C 应先于 A 和 B 获得相同的资源分配,B 获得的资源是A 所获资源的 1/3
头部压缩
- HTTP/2 使用 HPACK 压缩请求头和响应头,可以极大减少头部开销,进而提高性能
- 早期版本的 HTTP/2 和 SPDY 使用 zlib 压缩,可以将所传输头数据的大小减小 85%~88%,但在 2012 年夏天,被攻击导致会话劫持,后面被更安全的HPACK 取代
服务器推送
- 服务器可以对一个客户端请求发送多个响应,除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端额外明确地请求
HTTP/2 的问题
队头阻塞(head of line blocking)
-
如下图红黄蓝三个数据流,如果队头红色数据流丢失,那么黄蓝数据流就会等红色数据流重传后再往下处理,造成这个问题的原因还是因其底层使用的是TCP
握手延迟
- 由于其采用的是 TCP 连接的,所以还是需要三次握手才能建立连接
HTTP/3
-
Google 觉得 HTTP/2 仍然不够快,于是就有了 HTTP/3,HTTP/3 由 Google 开发,弃用了 TCP,改为使用基于 UDP 协议的 QUIC 协议实现, QUIC(Quick UDP Internet Connections),译为快速 UDP 网络连接,由 Google 开发,在 2013 年实现,于 2018 年从HTTP-over-QUIC改名为 HTTP/3
- HTTP/3 基于 UDP,如何保证可靠传输?
- 由 QUIC 来保证
- 为什么 Google 不开发一个新的不同于 TCP、UDP 的传输层协议?
- 目前世界上的网络设备基本只认识 TCP、UDP
- 如果要修改传输层,意味着操作系统的内核也要修改
- 另外,由 IETF 标准化的许多 TCP 新特性都因缺乏广泛支持而没有得到广泛的部署和使用
- 因此,想要开发并应用一个新的传输层协议,是及其困难的一件事情
- HTTP/3 目前还没有形成最终标准,还在改进当中
特性
连接迁移
- TCP 基于4 要素(源 IP、源端口、目标 IP、目标端口),切换网络时有一个要素发生变化,就会导致连接发生变化,当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间
- QUIC 的连接不受 4 要素的影响,当 4 要素发生变化时,原连接依然维持。QUIC 连接不以 4 要素作为标识,而是使用一组 Connection ID(连接 ID)来标识一个连接,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持,比如: 当设备连接到 Wi-Fi 时,将进行中的下载从蜂窝网络连接转移到更快速的 Wi-Fi 连接,当 Wi-Fi 连接不再可用时,将连接转移到蜂窝网络连接
HTTP/3 的问题
操作系统内核、CPU 负载
- 据 Google 和 Facebook 称,与基于 TLS 和 HTTP/2 相比,它们大规模部署的 QUIC 需要近 2 倍的 CPU 使用量。
- Linux 内核的 UDP 部分没有得到像 TCP 那样的优化,因为传统上没有使用 UDP 进行如此高速的信息传输,TCP 和 TLS 有硬件加速,而这对于 UDP 很罕见,对于 QUIC 则基本不存在
网友评论