八股文
计算机网络
HTTP 相关
HTTP 定义
HTTP 是一个在计算机中「两点」之间「传输」文字/图片/音频等「超文本」的「约定和规范」
常见的 HTTP 状态码有哪些?
- 1×× : 请求处理中,请求已被接受,正在处理
- 属于提示类信息,是一种中间状态,实际中用的比较少
- 100-Continue:继续
- 2×× : 请求成功,请求被成功处理 200 OK
- 200-OK:成功。如果是非 HEAD 请求,响应头会有 body 数据
- 202-Accepted
- 已接受。已经接受请求,但未处理完成
- 204-No Content:与 200 基本相同,但响应没有 body 数据
- 206-Partial Content:应用于 HTTP 断点续传,返回的 body 是资源的一部分
- 3×× : 重定向,要完成请求必须进行进一步处理
- 301-Moved Permanently:永久重定向
- 302-Found:临时重定向
- 301 和 302 都会在响应头中添加字段 Location 表明 URL
- 304-Not Modified:已缓存(资源没修改,用之前缓存就行)
- 4×× : 客户端错误,请求报文不合法
- 400-Bad Request:请求有语法问题
- 401-Unauthorized:缺乏身份验证
- 403-Forbidden:服务器禁止访问资源
- 这个状态类似于
401,但进入403状态后即使重新验证也不会改变该状态。该访问是长期禁止的,并且与应用逻辑密切相关
- 这个状态类似于
- 404-Not Found:请求的资源在服务器上找不到
- 5×× : 服务器端错误,服务器不能处理合法请求
- 500-Internal Server Error:发生错误(比较笼统)
- 501-Not Implemented:该功能还不支持
- 502-Bad Gateway:服务器作为网关/代理返回,表示服务器自身工作正常,但访问后端服务器发送错误
- 503-Service Unavailable:当前忙,暂时无法响应
- 此响应代码表明 HTTP 服务器正常,只是下层 web 服务不能正常工作
- 最可能的理由是资源不足:服务器突然收到太多请求,以至于无法全部处理
- 由于此问题多半由客户端反复发送请求造成,因此 HTTP 服务器可以选择拒绝客户端请求并返回 503
- 重试
- 服务器可以在响应头的 Retry-After 字段告知客户端何时可以重试
- 504-Gateway Timeout:网关超时
- 502 和 504 的区别
- 从网络角度,502 已经与后端建立了连接,但超时(电话打通了,但没人接);
- 504 与后端连接未建立,超时(电话没打通)。
- 502 和 504 的区别
http 中常见的 header 字段有哪些?
- 通用首部字段
- 请求/响应报文都会使用的首部
- Cache-Control 字段
- 控制缓存的工作机制
- no-cache/max-age=.../public/private
- Connection 字段
- 控制不再转发给代理的首部字段
- 管理持久连接
- 控制网络连接在当前会话完成后是否仍然保持打开状态。如果发送的值是
keep-alive,则连接是持久的,不会关闭,允许对同一服务器进行后续请求。
- Date 字段
- 创建 HTTP 报文的日期和时间
- 请求首部字段
- Accept 字段
- 客户端接受的媒体类型和优先级(加权)
- text/html q=0.3,text/plain
- Accept-Encoding 字段
- 客户端接受的内容编码和优先级(加权)
- gzip
- Authorization 字段
- 客户端的认证信息
- Host 字段
- 请求的资源所处的互联网主机名和端口号
- HTTP1.1 规定的唯一一个必须包含的请求首部字段
- Range 字段
- 获取指定资源范围
- Referer 字段
- 可以用来防御 CSRF 攻击
- 告知服务器请求的原始资源 URI,可以知道 URI 是从哪个 Web 页面发起的
- User-Agent 字段
- 浏览器/用户代理等信息
- Cookie 字段
- 告知服务端,想获取 Cookie 支持
- Status=enable
- Accept 字段
- 响应首部字段
- Accept-Ranges 字段
- 能否处理范围请求
- bytes-- 能,none-- 不能
- Age 字段
- 源服务器多少秒前创建响应,单位为秒
- Location 字段
- 引导至指定 URL(重定向)
- Set-Cookie 字段
- 告知客户端信息
- NAME, expires, path, domain, HttpOnly
- HttpOnly 使得 JavaScript 脚本无法获得 Cookie,从而防止了跨站脚本攻击(XSS)
- Accept-Ranges 字段
- 实体首部字段
- 针对请求 / 响应报文的实体部分使用的首部
- Allow 字段
- 服务端允许的 HTTP 方法
- Content-Encoding 字段
- 服务端内容编码格式
- Content-Length 字段
- 实体主题部分大小,单位字节
- Content-Range 字段
- 针对范围请求,指定返回实体的部分
- Content-Type 字段
- 实体主题内对象的媒体类型
请求方式
GET 与 POST 的区别
GET 一般用来从服务器上获取资源,POST 一般用来创建资源;
- GET 是幂等的,即读取同一个资源,总是得到相同的数据,而 POST 不是幂等的。
- 从请求参数形式上看,GET 请求的数据会附在
URL 之后;而 POST 请求会把提交的数据则放置在是 HTTP 请求报文的请求体中。 - POST 的安全性要比 GET 的安全性高,因为 GET 请求提交的数据将明文出现在 URL 上,而 POST 请求参数则被包装到请求体中,相对更安全。
- GET 的历史参数会保留在浏览器历史中
- GET 请求的长度受限于浏览器或服务器对 URL 长度的限制,允许发送的数据量比较小,而 POST 请求则是没有大小限制的。
从攻击的角度,无论是 GET 还是 POST 都不够安全,因为 HTTP 本身是明文协议。每个 HTTP 请求和返回的每个 byte 都会在网络上明文传播,不管是 url,header 还是 body。
为了避免传输中数据被窃取,必须做从客户端到服务器的端端加密。业界的通行做法就是 HTTPS —— 即用 SSL 协议协商出的密钥加密明文的 http 数据。
HEAD
- HEAD 方法与 GET 方法类似,也是 请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。
- HEAD 方法可以看做是 GET 方法的一个 “简化版” 或者 “轻量版”。因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。
- 比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。
HTTP 0.9/1.0/1.1
HTTP 0.9
- 第一个版本,极简单
- 只允许 get
- 没有请求头,因此只支持纯文本;
- 具有典型的无状态性,每个事务独立处理,处理完后就释放
HTTP 1.0
- 支持请求头
- 响应对象不止文本
- 支持 GET/HEAD/POST 方法
- 支持长连接(但默认短)
- 支持缓存
- 身份认证
HTTP 1.1
- 持久连接 / 长连接
- 在一个 TCP 连接 上可以传送多个 HTTP 请求和响应。使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
- 请求管道化
- 增加缓存处理(新的字段如 cache-control)
- 增加 Host 字段、支持断点传输等
- 提供范围请求功能,减少带宽浪费
HTTP 2.0/3.0
HTTP 2.0
-
二进制分帧
- 将响应报文划分为两个帧,首部 Header 和消息负载 Payload,并采用二进制编码
-
-
头部压缩
- HTTP1.1 中 Header 部分字段固定、字段值重复、字段是 ASCII 码,效率低
-
HTTP2.0 使用 HPACK 算法压缩头部
-
静态表/静态字典
- 为高频出现在头部的字符串建立了一张静态表,共 61 组
- 动态表/动态字典
- 不在静态表范围内的头部字符串自行构建动态表,Index 从 62 起
- 前提:必须是同一个连接上,重复传输完全相同的 HTTP 头部
- 动态表过大,占用内存过大,影响性能
- 通过 http2_max_requests 配置,限制一个连接上传输的请求数量。请求数达到上限,则关闭 HTTP2.0 连接来释放内存
-
Huffman 编码
-
-
- HTTP1.1 中 Header 部分字段固定、字段值重复、字段是 ASCII 码,效率低
-
并发传 输(多路复用 / 连接共享)
- 多路复用前,文件是串行传输的,请求 a 文件,b 文件只能等待,并且连接数过多。引入多路复用,a 文件 b 文件可以同时传输。
- 队头阻塞问题
- HTTP1.1 的队头阻塞
- HTTP1.1 基于请求 - 响应模型,同一个连接中,HTTP 收到上一个请求的响应才能开始下一个请求
- HTTP2.0 在 HTTP 层面解决了 1.1 的队头阻塞问题,但 TCP 层仍然存在队头阻塞问题,彻底解决需要放弃使用 TCP => HTTP3.0
- HTTP1.1 的队头阻塞
- 三个概念
- Stream,Message,Frame
- 一个 TCP 连接中包含一个或多个 Stream
- Stream 里包含一个或多个 Message,Message 对应 HTTP1.1 中的请求或响应,由 HTTP 头部和体组成
- Message 里包含一个或多个 Frame,Frame 为 HTTP2.0 最小单位,以二进制格式存放 HTTP1.1 中的头部和体
- HTTP2.0 中,不同的 Stream 的帧可以乱序发送,因此可以并发不同的 Stream
- 原因在于每个帧的头部都会携带 Stream ID,而同一 Stream 内部的帧必须是严格有序的
- 可以类比 kafka 中的分区(内部的帧数据)有序而不保证主题(Stream)有序
- 原因在于每个帧的头部都会携带 Stream ID,而同一 Stream 内部的帧必须是严格有序的
- CS 双方都可以建立 Stream,客户端 StreamID 必须为奇数,服务器为偶
- 可以通过帧头的标志位对每个 Stream 设置优先级
- 例如先传 HTML/CSS,再传图片,提高用户体验
- HTTP2.0 实现 100 个并发 Stream,只需要建立一次 TCP 连接;而 1.1 需要建立 100 次,每个连接都要经过 TCP 握手、慢启动、SSL/TLS 握手
- Nginx 中可以通过 http2_max_concurrent_streams 配置 Stream 上限
-
服务器主动推送
- HTTP1.1 不支持服务器主动推送资源,只能客户端发起请求
- 服务器推送资源时,通过 PUSH_PROMISE 帧传输 HTTP 头部,并通过 Promised Stream ID 告知客户端,接下来会在哪个偶数 Stream ID 中发送消息体
HTTP 3.0
- HTTP2.0 的问题
- 队头阻塞
- 一个 TCP 连接中包含多个请求,当 TCP 丢包,需要等待重传,就会阻塞该 TCP 连接中的所有请求
- TCP 和 TLS 的握手延迟
- 发起 HTTP 请求需要经过 TCP 三次握手和 TLS(1.2)四次握手,总共需要 3 个 RTT 时延才能发出请求数据
- 拥塞控制的慢启动
- 网络迁移需要重新连接
- IP 地址或端口变动,需要重新握手
- 队头阻塞
- QUIC
- 0RTT
- 无队头阻塞
- QUIC 协议也是可以在同一连接上并发多个 Stream
- UDP 不关心数据包顺序和丢包问题
- QUIC 保证数据包可靠,每个数据包都有一个序号唯一标识。当某个数据包丢失,即使该 Stream 中的其他数据包到达,数据也无法被读取,直到重传丢失的包成功
- 相比于 HTTP2.0,多个 Stream 间没有依赖,丢包只会影响当前 Stream
- 更快的连接建立
- HTTP3.0 传输前需要 QUIC 握手, 需要 1RTT,目的是确认双方的连接 ID
- QUIC 内部包含了 TLS,并且使用的是 TLS1.3,因此只需一个 RTT 就可以同时完成建立连接与密钥协商。
- 在第二次连接时,数据包可以和握手信息一起发送,达到 0RTT
- 连接迁移
- QUIC 协议中通过连接 ID 标识双方,因此 IP/端口变更后,只要仍有上下文信息(连接 ID、TLS 密钥),就可以复用原连接
- HTTP3.0 的帧头只有类型和长度两个字段
- 头部压缩,升级为 QPACK 算法,和 HPACK 类似。
- 静态表由 61 项扩大到 91 项
- 由于动态表具有时序性,如果首次请求丢包,导致无法解码出 HPACK 头部,将阻塞请求,QPACK 解决了这一问题
- QUIC 有两个特殊的单向流,一个是 QPACK Encoder Stream,用来将一个 KV 传递给对方;另一个 QPACK Decoder Stream,用来响应,告诉对方刚才的 KV 已经更新到本地动态表中,从而确认加入动态表成功
- 我的理解:因为动态表的确能节约很大一部分编码、传输上的开销,所以即使额外维护两个单向流,也是值得的。本质上是一种 trade-off
HTTP 缓存
- 客户端将第一次请求的 URL 和响应数据分别作为 KV,保存在本地磁盘
- 服务端在回应时会通过头部字段返回估算的数据过期时间
- 若过期,则请求头带 Etag,附上第一次响应头的摘要
- 服务端进行判断,若仍有效,则返回不含有消息体 body 的 304 Not Modified,这样一来就减少了响应资源的传输带宽
HTTPS
HTTPS 协议(HyperText Transfer Protocol over Secure Socket Layer):可以理解为 HTTP + SSL/TLS
HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL,用于安全的 HTTP 数据传输。

SSL/TLS 握手
- SSL(Secure Socket Layer,安全套接字层):SSL 协议位于 TCP/IP 协议与各种应用层协议之间,为数据通讯提供安全支持。
- TLS(Transport Layer Security,传输层安全):其前身是 SSL。
HTTPS 的连接过程 RSA (非对称加密算法)“四次握手”
- ClientHello
- 发送:客户端使用的 TLS 版本号、支持的密码套件、随机数 Client Random
- ServerHello
- 发送:确认 TLS 版本号是否支持、从密码套件中选择一个(密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法)、随机数 Server Random
- 发送:服务端的证书(包括公钥)
- Server Hello Done
- 客户端回应
- 通过证书链验证服务端身份、随机数 pre-master,并用服务器公钥加密,发送给服务端,双方根据三个随机数生成会话密钥 Master Secret,之后开始使用密钥进行通话
- 再把之前发送过的数据的摘要,用会话密钥加密发送给服务端,确认之前的信息没有被篡改
- 服务器最后回应
- 同客户端回应
- 缺陷
- 不支持前向保密
- 长期使用的主密钥泄漏会导致过去的会话密钥泄漏
- 客户端传随机数给服务端时用的是公钥加密,一旦服务器私钥泄漏,所有通讯密文都会被破解
- 不支持前向保密
