Catalog
- HTTP 协议
1.1 特性
1.2 HTTP 报文
1.3 会话
1.4 缓存
1.5 缺点
1.6 HTTP/1.1
1.7 HTTP/2.0
1.8 HTTP/3.0
1.9 HTTPS - DNS 协议
2.1 DNS 查找过程
2.2 递归查询和迭代查询
2.3 HTTPDNS - TCP 协议
3.1 OSI 网络模型
3.2 TCP 数据包大小
3.3 TCP 报文首部字段注析
3.4 三次握手
3.5 四次挥手
3.6 重传机制
3.7 滑动窗口
3.8 拥塞控制
3.9 快速打开(TFO)
3.10 TCP keep-alive 机制
3.11 安全
3.12 TCP 与 UDP 的区别 - IP 协议
4.1 特性
4.2 IP 报头
4.3 IP 地址类型
4.4 IP 地址分类
书单
- 《网络是怎样连接的》
- 《图解 HTTP》
- 《图解 TCP/IP》
- 《透视 HTTP 协议》
- 《浏览器原理》
真的会了吗?
- [缓存相关]
- 讲一讲强缓存和协商缓存?
- 有了【Last-Modified, If-Modified-Since】为何还要有【ETag、If-None-Match】
- 有关 HTTP 缓存的首部字段说一下
- [TCP/IP 相关]
- HTTP 和 TCP 的关系
- TCP 三次握手和四次挥手?以其存在意义。为什么挥手要多一次。(既保证了可靠传输(两次不可靠,因此要三次),且效率最高(成本最低)。)
- 在一次传输中它是如何保证每个数据包之间的顺序的?
- [HTTP 相关]
- HTTP 中的 keep-alive 有了解吗?
- 说说 HTTP/1.1 长连接、管线化
- HTTP/2 特性
- HTTP/2 对比 HTTP/1.1
- HTTP/2 都有哪些特性?头部压缩的原理?
- HTTP/2 是怎么解决队头阻塞的
- HTTP/2 是如何压缩头部的
- 具体说一下 HTTP/2 中的多路复用
- HTTPS 握手
- HTTPS 请求的什么时候用的对称加密什么时候非对称加密
- HTTP3 特性
- GET 和 POST 的区别
- GET 就一定是幂等的吗?
- 状态码。301/302/303/304/401/403/404/500/503?
- 状态码。204 和 304 分别有什么作用?
- HTTP 和 HTTPS 握手差异?
- 为什么说 HTTPS 比 HTTP 安全呢
- 简单讲了一下非对称加密的握手过程
- 证书签名过程和如何防止被串改
- TCP/IP 网络分层模型是怎样分层的
- OSI 网络分层模型是怎样分层的
- TCP 和 UDP 区别
- HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2、HTTP/3 各版本之间的区别?
- 详细说说 DNS
- [综合相关]
- CSRF 跨站请求伪造和 XSS 跨站脚本攻击是什么?
- 如果让你去实现一个 CSRF 攻击你会怎做?
- 如果使用 jsonp 的话会有什么安全问题吗?
- 你是如何解决跨域的?都有几种?
- 为什么说 GET 会留下历史记录?
- 从“在浏览器输入域名”到“页面静态资源完全加载”的整个流程
- 假设有两个子项目, 他们需要共用同一个用户体系如何保证关掉页面之后打开另一个项目用户还是登录状态?
1. HTTP 协议(应用层)
HTTP 的历史
时间 | 协议 | 特性 |
---|---|---|
1991 | HTTP/0.9 | 只接受 GET 方法,不支持请求头 |
1996 | HTTP/1 | 基本成型,支付富文本、header、状态码、缓存等 |
1999 | HTTP/1.1 | 使用了 20 年的主流标准,支持长连接、管线化、分块传输 |
2009 | SPDY | HTTP/2 前身 |
2013 | QUIC | 基于 UDP 实现 TCP+HTTP/2 并优化 |
2015 | HTTP/2 | 二进制分帧、多路复用、头部压缩、服务器推送等 |
2018 | HTTP/3 | QUIC 更名为 HTTP/3 |
1.1 特性
- HTTP 协议构建于 TCP/IP 协议之上的应用层规范
- HTTP 是无状态无连接的
1.2 HTTP 报文
报文的结构由报文首部、空行、报文主体三部分组, 并不一定要有报文主体。
| 报文首部 | -------- | 空行 | 报文主体 |
请求报文
报文结构 | 格式 | 实例 |
---|---|---|
请求行 | method URL version | POST /admin/v1/platform/user/get HTTP/1.1 |
首部字段 | headers | Content-Type: application/json |
空行(CR+LF) | 空行(CR+LF) | 空行(CR+LF) |
报文主体 | entity-body | {"name":"adi"} |
响应报文
报文结构 | 格式 | 实例 |
---|---|---|
状态行 | 协议版本 状态码 描述短语 | HTTP/1.1 200 OK |
首部字段 | headers | Content-Type: text/html; charset=UTF-8 |
空行(CR+LF) | CR+LF | CR+LF |
报文主体 | entity-body | html... |
首部字段的四种类型以及其他类型
- 通用首部字段
- Cache-Control 控制缓存的行为
- Connection 逐跳首部、连接的管理
- Date 创建报文的日期时间
- Pragma 报文指令
- Trailer 报文末端的首部一览
- Transfer-Encoding 指定报文主体的传输编码方式
- Upgrade 升级为其他协议
- Via 代理服务器的相关信息
- Warning 错误通知
- 请求首部字段
- Accept 用户代理可处理的媒体类型
- Accept-Charset 优先的字符集
- Accept-Encoding 优先的内容编码
- Accept-Language 优先的语言(自然语言)
- Authorization Web 认证信息
- Expect 期待服务器的特定行为
- From 用户的电子邮箱地址
- Host 请求资源所在服务器
- If-Match 比较实体标记(ETag)
- If-Modified-Since 比较资源的更新时间
- If-None-Match 比较实体标记(与 If-Match 相反)
- If-Range 资源未更新时发送实体
- Byte 的范围请求
- If-Unmodified-Since
- 比较资源的更新时间(与 If-Modified-Since 相反)
- Max-Forwards 最大传输逐跳数
- Proxy-Authorization 代理服务器要求客户端的认证信息
- Range 实体的字节范围请求
- Referer 对请求中 URI 的原始获取方 TE 传输编码的优先级
- User-Agent HTTP 客户端程序的信息
- 响应首部字段
- Accept-Ranges 是否接受字节范围请求
- Age 推算资源创建经过时间
- ETag 资源的匹配信息
- Location 令客户端重定向至指定 URI
- Proxy-Authenticate 代理服务器对客户端的认证信息
- Retry-After 对再次发起请求的时机要求
- Server HTTP 服务器的安装信息
- Vary 代理服务器缓存的管理信息
- WWW-Authenticate 服务器对客户端的认证信息
- 实体首部字段
- Allow 资源可支持的 HTTP 方法
- Content-Encoding 实体主体适用的编码方式
- Content-Language 实体主体的自然语言
- Content-Length 实体主体的大小(单位:字节)
- Content-Location 替代对应资源的 URI
- Content-MD5 实体主体的报文摘要
- Content-Range 实体主体的位置范围
- Content-Type 实体主体的媒体类型
- Expires 实体主体过期的日期时间
- Last-Modified 资源的最后修改日期时间
- 其他
- 可能包含 HTTP 的 RFC 里未定义的首部(Cookie 等)。
常见状态码:
- 200 OK 客户端请求成功
- 301 Moved Permanently 请求永久重定向
- 302 Moved Temporarily 请求临时重定向
- 304 Not Modified 文件未修改, 可以直接使用缓存的文件。
- 400 Bad Request 由于客户端请求有语法错误, 不能被服务器所理解。
- 401 Unauthorized 请求未经授权。这个状态代码必须和 WWW-Authenticate 报头域一起使用
- 403 Forbidden 服务器收到请求, 但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因
- 404 Not Found 请求的资源不存在, 例如, 输入了错误的 URL
- 500 Internal Server Error 服务器发生不可预期的错误, 导致无法完成客户端的请求。
- 503 Service Unavailable 服务器当前不能够处理客户端的请求, 在一段时间之后, 服务器可能会恢复正常。
POST 提交数据的方式
HTTP 协议中规定 POST 提交的数据必须在 body 部分中, 但是协议没有规定数据使用哪种编码方式或者数据格式。
数据发送到服务端后, 服务端根据请求头中的 content-type 字段获知请求头用何种方式编码, 再使用相应方式解码。
- application/x-www-form-urlencoded (default)
- multipart/form-data
- text/plain
- text/xml
- application/x-protobuf
- application/json
GET 与 POST 的区别
- GET 在浏览器回退时是无害的, 而 POST 会再次提交请求。
- GET 产生的 URL 地址可以被 Bookmark, 而 POST 不可以。
- GET 请求会被浏览器主动 cache, 而 POST 不会, 除非手动设置。
- GET 请求只能进行 url 编码, 而 POST 支持多种编码方式。
- GET 请求参数会被完整保留在浏览器历史记录里, 而 POST 中的参数不会被保留。
- GET 请求在 URL 中传送的参数是有长度限制的, 而 POST 么有。
- 对参数的数据类型, GET 只接受 ASCII 字符, 而 POST 没有限制。
- GET 比 POST 更不安全, 因为参数直接暴露在 URL 上, 所以不能用来传递敏感信息。
- GET 参数通过 URL 传递, POST 放在 Request body 中。
1.3 会话
什么是会话?
客户端打开与服务器的连接发出请求到服务器响应客户端请求的全过程称之为会话。为什么需要会话跟踪?
浏览器与服务器之间的通信是通过 HTTP 协议进行通信的, 而 HTTP 协议是”无状态”的协议, 它不能保存客户的信息, 即一次响应完成之后连接就断开了, 下一次的请求需要重新连接, 这样就需要判断是否是同一个用户, 所以才有会话跟踪技术来实现这种要求。会话跟踪常用的方法:
URL 重写
URL(统一资源定位符)是 Web 上特定页面的地址, URL 重写的技术就是在 URL 结尾添加一个附加数据以标识该会话,把会话 ID 通过 URL 的信息传递过去, 以便在服务器端进行识别不同的用户。隐藏表单域
将会话 ID 添加到 HTML 表单元素中提交到服务器, 此表单元素并不在客户端显示Cookie
Cookie 是 Web 服务器发送给客户端的一小段信息, 客户端请求时可以读取该信息发送到服务器端, 进而进行用户的识别。对于客户端的每次请求, 服务器都会将 Cookie 发送到客户端,在客户端可以进行保存,以便下次使用。客户端可以采用两种方式来保存这个 Cookie 对象, 一种方式是保存在客户端内存中, 称为临时 Cookie, 浏览器关闭后这个 Cookie 对象将消失。另外一种方式是保存在客户机的磁盘上, 称为永久 Cookie。以后客户端只要访问该网站, 就会将这个 Cookie 再次发送到服务器上, 前提是这个 Cookie 在有效期内, 这样就实现了对客户的跟踪。Cookie 是可以被客户端禁用的。Session
每一个用户都有一个不同的 session, 各个用户之间是不能共享的, 是每个用户所独享的, 在 session 中可以存放信息。在服务器端会创建一个 session 对象, 产生一个 sessionID 来标识这个 session 对象, 然后将这个 sessionID 放入到 Cookie 中发送到客户端, 下一次访问时, sessionID 会发送到服务器, 在服务器端进行识别不同的用户。Session 的实现依赖于 Cookie, 如果 Cookie 被禁用, 那么 session 也将失效。
1.4 缓存
缓存类别
- 强缓存
- Expires 、 Cache-Control
- 协商缓存
- Last-Modified 、 ETag
缓存字段
Expires:
HTTP/1.0, 值为服务器设定的 GMT 时间(Wed Sep 23 2020 11:17:01 GMT)。
当客户端时间小于服务器设定的时间时使用缓存。
Cache-Control:
HTTP/1.1, 可选值如下:
请求首部时可用:
可选值 | 说明 | tips |
---|---|---|
no-cache | 在发布缓存副本之前, 强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)。 | 原服务器判断缓存是新鲜的会返回 304 状态码 |
no-store | 缓存不应存储有关客户端请求或服务器响应的任何内容, 即不使用任何缓存。 | 直接不缓存 |
max-age= |
告知服务器客户端希望接收一个存在的时间(Age)不大于 delta-seconds 秒的资源 | |
max-stale[= |
表明客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数, 表示响应不能已经过时超过该给定的时间。 | |
min-fresh= |
表示客户端希望获取一个能在指定的秒数内保持其最新状态的响应。 | |
no-transform | 不得对资源进行转换或转变。Content-Encoding、Content-Range、Content-Type 等 HTTP 头不能由代理修改。 | |
only-if-cached | 表明客户端只接受已缓存的响应, 并且不要向原始服务器检查是否有更新的拷贝。 | |
cache-extension | 自定义拓展值 |
响应首部时可用:
可选值 | 说明 | tips |
---|---|---|
public | 表明响应可以被任何对象(包括:发送请求的客户端, 代理服务器, 等等)缓存 | |
private | 表明响应只能被单个用户缓存, 不能作为共享缓存(即代理服务器不能缓存它)。 | |
no-cache | 在发布缓存副本之前, 强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)。 | 原服务器判断缓存是新鲜的会返回 304 状态码 |
no-store | 缓存不应存储有关客户端请求或服务器响应的任何内容, 即不使用任何缓存。 | 直接不缓存 |
only-if-cached | 表明客户端只接受已缓存的响应, 并且不要向原始服务器检查是否有更新的拷贝。 | |
must-revalidate | 一旦资源过期(比如已经超过 max-age), 在成功向原始服务器验证之前, 缓存不能用该资源响应后续请求。 | |
proxy-revalidate | 与 must-revalidate 作用相同, 但它仅适用于共享缓存(例如代理), 并被私有缓存忽略。 | |
max-age | 设置缓存存储的最大周期, 超过这个时间缓存被认为过期(单位秒)。 | 与 Expires 相反, 时间是相对于请求的时间。 |
s-maxage | 覆盖 max-age 或者 Expires 头, 但是仅适用于共享缓存(比如各个代理), 私有缓存会忽略它。 | |
cache-extension | 自定义拓展值 |
Cache-Control 允许自由组合可选值, 例如:
Cache-Control: max-age=3600, must-revalidate
Pragma:
HTTP/1.0, 值为 node-cache
。
该字段会知会客户端不要对该资源读缓存, 即每次都得向服务器发一次请求才行。
优先级:
Pragma > Cache-Control > Expires
Last-Modified:
HTTP/1.0, 值为Last-Modified: Wed Sep 23 2020 11:17:01 GMT
对应请求首部字段:
字段名 | 说明 | 值 |
---|---|---|
If-Modified-Since | 该请求首部会传递给服务器端进行最后修改时间的比对, 当值一致时返回 304 和响应头。 | Wed Sep 23 2020 11:17:01 GMT |
If-Unmodified-Since | 告诉服务器, 若 Last-Modified 没有匹配上(资源在服务端的最后更新时间改变了), 则应当返回 412(Precondition Failed) 给客户端 | Wed Sep 23 2020 11:17:01 GMT |
ETag:
HTTP/1.1, 值为根据资源计算得出的唯一标志符。
例如: Etag: 5d8c72a5edda8d6a:3239
对应请求首部字段:
字段名 | 说明 | 值 |
---|---|---|
If-None-Match | 该请求首部会传递给服务器端进行 ETag 的比对, 当值一致时返回 304 和响应头。 | 5d8c72a5edda8d6a:3239 |
If-Match | 告诉服务器如果没有匹配到 ETag, 或者收到了“*”值而当前并没有该资源实体, 则应当返回 412(Precondition Failed) 状态码给客户端。 | 5d8c72a5edda8d6a:3239 |
缓存头部对比
头部 | 优势和特点 | 劣势和问题 |
---|---|---|
Expires | 1、HTTP 1.0 产物, 可以在 HTTP 1.0 和 1.1 中使用, 简单易用。2、以时刻标识失效时间。 | 1、时间是由服务器发送的(UTC), 如果服务器时间和客户端时间存在不一致, 可能会出现问题。2、存在版本问题, 到期之前的修改客户端是不可知的。 |
Cache-Control | 1、HTTP 1.1 产物, 以时间间隔标识失效时间, 解决了 Expires 服务器和客户端相对时间的问题。2、比 Expires 多了很多选项设置。 | 1、HTTP 1.1 才有的内容, 不适用于 HTTP 1.0 。2、存在版本问题, 到期之前的修改客户端是不可知的。 |
Last-Modified | 1、不存在版本问题, 每次请求都会去服务器进行校验。服务器对比最后修改时间如果相同则返回 304, 不同返回 200 以及资源内容。 | 1、只要资源修改, 无论内容是否发生实质性的变化, 都会将该资源返回客户端。例如周期性重写, 这种情况下该资源包含的数据实际上一样的。2、以时刻作为标识, 无法识别一秒内进行多次修改的情况。3、某些服务器不能精确的得到文件的最后修改时间。 |
ETag | 1、可以更加精确的判断资源是否被修改, 可以识别一秒内多次修改的情况。2、不存在版本问题, 每次请求都回去服务器进行校验。 | 1、计算 ETag 值需要性能损耗。2、分布式服务器存储的情况下, 计算 ETag 的算法如果不一样, 会导致浏览器从一台服务器上获得页面内容后到另外一台服务器上进行验证时发现 ETag 不匹配的情况。 |
1.5 缺点
- 通信使用明文(不加密), 内容可能会被窃听(TCP/IP)
- 不验证通信方的身份, 因此有可能遭遇伪装(客户端和服务端都有可能)
- 无法证明报文的完整性, 有可能会被篡改(中间人攻击)
1.6 HTTP/1.1
长连接
特性:
- 我们知道 HTTP 协议采用“请求-应答”模式,每个请求/应答客户和服务器都要新建一个连接, 完成之后立即断开连接(HTTP 协议无连接特性);
- 当使用了 Keep-Alive 模式(长连接)时, 数据传输完成了保持 TCP 连接不断开(不发 RST 包、不四次握手), 使客户端到服务端的链接持续有效;
历史:
- 在 HTTP/1.0 中, 默认使用短连接, 提出长连接(也叫持久连接)的概念, 但当时仅提供初步的支持。
- 在 HTTP/1.1 中, 默认使用长连接, 用以保持连接特性。使用长连接的 HTTP 协议, 会在响应头加入这行代码:Connection:keep-alive, 只有设置 Connection: close 才会关闭。
长连接短连接操作过程:
- 短连接: 建立连接 -> 数据传输 -> 关闭连接 , 建立连接 -> 数据传输 -> 关闭连接
- 长连接: 建立连接 -> 数据传输...(保持连接)...数据传输 -> 关闭连接
长连接过期时间:
- 设置 keep-Alive: timeout=20 , max = 10。 timeout=20, 表示这个 TCP 通道可以保持 20 秒, max=10, 表示这个长连接最多接收 10 次请求就断开。
识别传输结束:
- 判断传输数据是否达到了 Content-Length 指示的大小;
- 动态生成的文件没有 Content-Length , 它是分块传输(chunked), 这时候就要根据 chunked 编码来判断, chunked 编码的数据在最后有一个空 chunked 块, 表明本次传输数据结束。
长连接与 WebSocket 的区别:
- HTTP 的长连接:HTTP/1.1 通过使用 Connection:keep-alive 进行长连接。在一次 TCP 连接中可以完成多个 HTTP 请求, 但是对每个请求仍然要单独发 header, Keep-Alive 不会永久保持连接, 它有一个保持时间, 可以在不同的服务器软件(如 Apache)中设定这个时间。这种长连接是一种“伪链接”, 而且只能由客户端发送请求, 服务端响应。
- WebSocket 的长连接, 是一个全双工的连接, 可由服务端主动发起信息。长连接第一次 TCP 链路建立之后, 后续数据可以双方都进行发送, 不需要发送请求头。
- HTTP/1.1 中双方并没有建立正真的连接会话, 服务端可以在任何一次请求完成后关闭。WebSocket 它本身就规定了是正真的、双工的长连接, 两边都必须要维持住连接的状态。
管线化
特性:
- 在长连接的基础上, 将多个 HTTP 请求并行发送, 在发送过程中不用等待响应, 解决了长连接中某个请求的响应没有及时返回导致后续请求无法发起的 HTTP 队头阻塞问题。
过程:
- 长连接:请求 1 -> 响应 1 -> 请求 2 -> 响应 2 -> 请求 3 -> 响应 3
- 管线化:请求 1 -> 请求 2 -> 请求 3 -> 响应 1 -> 响应 2 -> 响应 3
限制:
- 初次创建连接不能启用管线机制
- 只有 GET 和 HEAD 请求可以进行管线化, 而 POST 则有所限制。
- 客户端和服务器端都要支持 HTTP/1.1 协议。
- HTTP /1.1 要求服务器端支持管线化, 但并不要求服务器端也对响应进行管线化处理, 只是要求对于管线化的请求不失败即可
- 由于上面提到的服务器端问题, 开启管线化很可能并不会带来大幅度的性能提升, 而且很多服务器端和代理程序对管线化的支持并不好, 因此现代浏览器如 Chrome 和 Firefox 默认并未开启管线化支持
编码提升传输速率
报文主体和实体主体的差异
在开始学习编码传输前, 我们需要了解 报文主体 和 实体主体。
报文主体是 HTTP 报文(报文头部、空行和报文主体)的一部分。
实体主体是由实体首部和实体主体组成, 作为请求或响应的有效载荷数据被传输。
通常, 报文主体等于实体主体。只有当传输中进行编码操作时, 实体主体的内容发生变化, 才导致它和报文主体产生差异。(tips: 这一块不好理解的话, 可以看看标题的 link)
传输内容编码
在传输数据时可以按照数据原貌直接传输, 但也可以在传输过程中通过编码提升传输速率。通过在传输时编码, 能有效地处理大量的访问请求。
过程:
- 客户端设置 Accept-Encoding 字段, 告知服务端当前支持的压缩编码。
- 服务端进行选择一种客户端支持的编码方式进行编码, 编码后在响应头添加 Content-Encoding 字段, 告知客户端使用哪种方式解码, 并修改响应头中的 Content-Length 告知实体编码后的长度。
常用的编码类型:
- gzip:表明实体采用 GNU zip 编码。
- compress:表明实体采用 Unix 的文件压缩程序。
- deflate:表明使用是用 zlib 的格式压缩的。
- br:表明实体使用 Brotli 算法的压缩格式。
- identity:表明没有对实体进行编码, 为默认值。
在这些编码类型中, 除了 identity 之外, 都是无损压缩, 他们都是需要可还原成原始的文本内容的。gzip 通常是效率最高的, 使用最广泛的。
分块传输编码
- 传输编码必须配合持久连接去使用, 为了在一个持久连接中, 将数据分块传输, 并标记传输结束而设计的.
- 传输编码使用 Transfer-Encoding 首部进行标记, 在最新的 HTTP/1.1 协议里, 它只有 chunked 这一个取值, 表示分块编码。
- 分块的格式:数据长度(16 进制)+ 分块数据。
- 如果还有额外的数据, 可以在结束之后, 使用 Trailer 进行拖挂传输额外的数据。
分块传输的规则:
- 每个分块包含一个 16 进制的数据长度值和真实数据。
- 数据长度值独占一行, 和真实数据通过 CRLF(\r\n) 分割。
- 数据长度值, 不计算真实数据末尾的 CRLF, 只计算当前传输块的数据长度。
- 最后通过一个数据长度值为 0 的分块, 来标记当前内容实体传输结束。
内容编码和传输编码结合
内容编码和传输编码一般都是配合使用的。
我们会先使用内容编码, 将内容实体进行压缩, 然后再通过传输编码分块发送出去。
客户端接收到分块的数据, 再将数据进行重新整合, 还原成最初的数据。
1.7 HTTP/2.0
HTTP/1.1 存在的问题:
1、TCP 连接数限制
同一域名, 浏览器限制了最多同时创建 6-8 个 TCP 连接, 当请求数过多时会被浏览器挂起等待。
2、线头阻塞问题
HTTP/1.0 时, 一个 TCP 连接只能处理一个请求;HTTP/1.1 默认开启长连接, 使得一个 TCP 连接可以处理多个请求, 它要求以 请求 1 -> 响应 1 -> 请求 2 -> 响应 2 顺序进行通讯, 当 请求 1 请求时间较长会阻塞住 请求, 造成 HTTP 队头阻塞问题。 2。于是出现了管线化技术, 它可以并发多个请求然后按照顺序返回响应, 请求 3 && 请求 4 -> 响应 3 && 响应 4, 不过管线化也没有很好的解决响应阻塞的问题, 当 响应 3 响应时间较长会阻塞住 响应 4 , 造成 HTTP 队头阻塞问题。
3、Header 体积优化问题
HTTP/1.x 每次通信都会携带一组头部, 用于描述这次通信的的资源、浏览器属性、cookie 等, 都会携带大量冗余头信息, 并且每次请求 header 基本不怎么变化, 尤其在移动端增加用户流量,在一定程度上增加了传输的成本。
4、明文传输的安全问题
HTTP1.x 在传输数据时, 所有传输的内容都是明文的, 存在网络窃听、篡改、冒充等风险。
SPDY 协议
对于 HTTP/1.x 存在的问题, 那时的前端们会使用雪碧图、域名分片等手法来优化用户体验。这些技巧一定程度上优化了用户体验, 但 HTTP 协议本身的限制也令人有些束手无策。为了进行根本性的改善, 需要有一些协议层面上的改动, 于是谷歌推出 SPDY 协议。
使用 SPDY 后, HTTP 协议等到了一下功能: 多路复用流, 压缩 HTTP 首部, 赋予请求优先级, 服务器推送, 服务器提示 等等, SPDY 的实践证明了这些优化的效果, 也最终带来 HTTP/2 的诞生。
二进制分帧层
HTTP/2 采用二进制格式传输数据, HTTP 1.x 使用的是文本格式, 二进制协议解析起来更高效。
概念:
- 流:流是连接中的一个虚拟信道, 可以承载双向的消息;每个流都有一个唯一的整数标识符(1、2…N)
- 帧:HTTP/2 数据通信的最小单位消息:指 HTTP/2 中逻辑上的 HTTP 消息。例如请求和响应等, 消息由一个或多个帧组成。
HTTP/2 中, 同域名下所有通信都在单个连接上完成, 该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送, 而消息又由一个或多个帧组成。多个帧之间可以乱序发送, 根据帧首部的流标识(identifier)可以重新组装。
多路复用
在 HTTP 1.x 中, 如果想并发多个请求, 必须使用多个 TCP 链接。
在 HTTP/2 中, 有了二进制分帧之后, HTTP /2 不再依赖 TCP 链接去实现多流并行。
特性:
- 同域名下所有通信都在单个连接上完成
- 单个连接可以承载任意数量的双向数据流
- 数据流以消息的形式发送, 而消息又由一个或多个帧组成, 多个帧之间可以乱序发送, 因为根据帧首部的流标识可以重新组装
服务端推送
浏览器发送一个请求, 服务器主动向浏览器响应并推送与这个请求相关的资源。
服务端推送主要针对资源内联进行优化。例如客户端请求获取 index.html,里面有内联的 index.css, , 这时服务器接收到 index.html 请求后, 会返回 index.html 和 index.css 给客户端, 客户端在解析 html 时就不需要在发送这些请求了。
服务端可以主动推送, 客户端也有权利选择是否接收。如果服务端推送的资源已经被浏览器缓存过, 浏览器可以通过发送 RST_STREAM 帧来拒收。主动推送也遵守同源策略, 换句话说, 服务器不能随便将第三方资源推送给客户端, 而必须是经过双方确认才行。
Header 压缩
HTTP/2 使用 HPACK 算法来压缩首部内容, 能够节省消息头占用的网络的流量。
HTTP 在每次通信是都会携带一组头部,大部分请求头变化不大(ua、cookie)。
为了减少这块的资源消耗并提升性能, HTTP/2 对这些首部采用了压缩策略:
- HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送;
- 首部表在 HTTP/2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新;
-
每个新的首部键-值对要么被追加到当前表的末尾,要么替换表中之前的值。
例如:下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。
应用层的重置连接
HTTP/1 通过设置 tcp segment 里的 reset flag 来通知对端关闭连接的。这种方式会直接断开连接,下次再发请求就必须重新建立连接。HTTP/2 引入 RST_STREAM 类型的 frame,可以在不断开连接的前提下取消某个 request 的 stream,表现更好。
流量控制
TCP 协议通过 sliding window 的算法来做流量控制。发送方有个 sending window,接收方有 receive window。HTTP/2 的 flow control 是类似 receive window 的做法,数据的接收方通过告知对方自己的 flow window 大小表明自己还能接收多少数据。只有 Data 类型的 frame 才有 flow control 的功能。对于 flow control,如果接收方在 flow window 为零的情况下依然更多的 frame,则会返回 block 类型的 frame,这张场景一般表明 HTTP/2 的部署出了问题。
更安全的 SSL
HTTP/2 使用了 tls 的拓展 ALPN 来做协议升级,除此之外加密这块还有一个改动,HTTP/2 对 tls 的安全性做了近一步加强,通过黑名单机制禁用了几百种不再安全的加密算法,一些加密算法可能还在被继续使用。如果在 ssl 协商过程当中,客户端和 server 的 cipher suite 没有交集,直接就会导致协商失败,从而请求失败。在 server 端部署 HTTP/2 的时候要特别注意这一点。
升级 HTTP/2 后哪些优化技巧可以弃用了?
- 雪碧图
- 域名分片
- 内联资源
HTTP/2 的缺陷
- TCP 以及 TCP+TLS 建立连接的延时
- TCP 三次握手、TLS 四次握手
- 队头阻塞并没有彻底解决 - TCP 队头阻塞
- HTTP/2 底层使用的是 TCP 协议, TCP 是一个按序传输的链条, 一旦传输双方网络中有一个数据包丢失, 或者网络出现中断, 整个 TCP 连接就会暂停, 丢失的数据包需要重新传输。
- 多路复用导致服务器压力上升
- 多路复用没有限制同时请求数。请求的平均数量与往常相同, 但实际会有许多请求的短暂爆发, 导致瞬时 QPS 暴增。
Reference
一文读懂 HTTP/2 特性
一文读懂 HTTP/2 及 HTTP/3 特性
HTTP/2 和 HTTP/1 速度对比
再谈 HTTP/2 性能提升之背后原理—HTTP2 历史解剖
HTTP/2 详解
硬核!30 张图解 HTTP 常见的面试题
1.8 HTTP/3.0
HTTP/3 是由 Google 开发协议 QUIC 改名而来。
RTT(Round-Trip Time):
往返时延。表示从发送端发送数据开始, 到发送端收到来自接收端的确认(接收端收到数据后便立即发送确认), 总共经历的时延。
QUIC 协议
QUIC 是基于 UDP 协议二次开发的协议,它整合了 TCP、TLS、HTTP/2 的优点并加以优化。
特性:
- 零 RTT 建立连接
- 连接迁移
- 队头阻塞/多路复用
- 拥塞控制
- 热插拔
- 前向纠错 FEC
- 单调递增的 Packet Number
- ACK Delay
- 流量控制
HTTP/3 的缺点
QUIC 太吃 CPU、内核处理 UDP 很慢、UDP 永远不会通
Reference
http3-explained
HTTP/3 原理与实践
深入解读 HTTP3 的原理及应用
https://www.jesuisundev.com/en/understand-http3-in-5-minutes
白话 http 队头阻塞
一文读懂 HTTP/1HTTP/2HTTP/3
1.9 HTTPS
HTTPS (HTTP Secure, 超文本传输安全协议) = HTTP + SSL/TLS(加密 ,认证 ,完整性验证)。
作用
HTTP 使用明文在网络进行传输, 带来了三大风险:
- 窃听风险: 第三方可以获知通信内容。
- 篡改风险: 第三方可以修改通信内容。
- 冒充风险: 第三方可以冒充他人身份参与通信。
HTTPS 通过加入安全套接层(SSL/TLS), 做到了: - 防窃听: 所有信息都是加密传播, 第三方无法窃听。
- 防篡改: 具有校验机制, 一旦被篡改, 通信双方会立刻发现。
- 防冒充: 配备身份证书, 防止身份被冒充。
SSL 与 TLS 协议
- SSL(Secure Socket Layer, 安全套接层)
- TLS(Transport Layer Security, 安全层传输协议)
SSL/TLS 建立在 TCP 协议之上, 在使用前需要与服务器进行三次握手建立连接。
步骤:
- TCP 三次握手
- SSL/TLS 四次握手
- 传输数据
- TCP 四次挥手
tips: TCP 三次握手与四次挥手在后面章节有详细介绍
SL/TLS 四次握手 基本过程
步骤:
- 客户端向服务器端索要证书取出公钥(非对称加密)并验证
- 双方协商生成"对话密钥"(对称加密)
- 双方采用"对话密钥"进行加密通信
SSL/TLS 四次握手 详细过程
步骤:
- TLS CLient: 生成第一个随机数, 发起要求加密通信的请求, 携带( 1.支持的协议版本 2.第一个随机数 3.支持的加密方法 4.支持的压缩方法 [5.请求的域名] )
- TLS Server: 生成第二个随机数, 回应客户端, 返回( 1.确定使用的协议版本 2.第二个随机数 3.确定使用的加密方法 4. 服务器证书 [5.是否需要客户端证书校验身份] )
- TLS CLient: 生成第三个随机数, 验证服务器返回的证书, 取出 服务器公钥 (非对称加密), 根据三个随机数计算出"对话密钥"保存。随后使用 服务器公钥 加密数据后发起请求, 请求携带( 1.第三个随机数 2.编码改变通知,表示随后通话将使用对话秘钥加密数据 3.握手结束通知,标识客户端握手结束,同同时返回前面所有内容的 hash, 用来供服务器校验 )
- TLS Server: 服务器接收到数据后用私钥解密获取客户端携带的数据, 根据三个随机数计算出"对话密钥"。回应客户端, 返回( 1.编码改变通知, 表示随后的信息都将用双方商定的加密方法和密钥发送 2.服务器握手结束通知, 表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的 hash 值, 用来供客户端校验)
tips:
- TLS CLient: 客户端请求
- TLS Server: 服务端响应
- 中括号的内容是可选的
- 以上说明的都是 TLS 1.2 协议的握手情况, 在 1.3 协议中, 首次建立连接只需要一个 RTT, 后面恢复连接不需要 RTT 了
TLS/1.3
TLS/1.3 相对 TLS/1.2 在安全与性能方面进行了一系列改进。
减少握手等待时间,将握手时间从 2-RTT 降低到 1-RTT,并且增加 0-RTT 模式。
1-RTT:
第 1 步,客户端发送 ClientHello 消息,该消息主要包括客户端支持的协议版本、会话 ID、密码套件、压缩算法、以及扩展消息(密钥共享、预共享密钥、预共享密钥模式);
第 2 步,服务端回复 ServerHello,包含选定的加密套件;发送证书给客户端;使用证书对应的私钥对握手消息签名,将结果发送给客户端;选用客户端提供的参数生成临时公钥,结合选定的参数计算出用于加密 HTTP 消息的共享密钥;服务端生成的临时公钥通过 KeyShare 消息发送给客户端;
第 3 步,客户端接收到 KeyShare 消息后,使用证书公钥进行签名验证,获取服务器端的临时公钥,生成会话所需要的共享密钥;
第 4 步,双方使用生成的共享密钥对消息加密传输,保证消息安全。
0-RTT:
TLS 1.3 的客户端连接到同样支持 TLS 1.3 的服务器时, 客户端会将收到服务器发送过来的 Ticket 通过相关计算,一起组成新的 预共享密钥,PSK (PreSharedKey)。客户端会将该 PSK 缓存在本地,在会话恢复时在 Client Hello 上带上 PSK 扩展,同时通过之前客户端发送的完成(finished)计算出恢复密钥 (Resumption Secret)通过该密钥加密数据发送给服务器。服务器会从会话 Ticket 中算出 PSK,使用它来解密刚才发过来的加密数据。至此完成了该 0-RTT 会话恢复的过程。
SSL 与 TLS 历史
协议/版本 | 说明 |
---|---|
SSL/1.0 | 1994 年, NetScape 公司设计了 SSL/1.0, 但是没发布 |
SSL/2.0 | 1995 年, NetScape 公司发布 SSL 2.0 版, 很快发现有严重漏洞 |
SSL/3.0 | 1996 年, SSL 3.0 版问世, 得到大规模应用 |
TLS/1.0 | 1999 年, 互联网标准化组织 ISOC 接替 NetScape 公司, 发布了 SSL 的升级版 TLS 1.0 版 |
TLS/1.1 | 2006 年, ISOC 发布升级版 |
TLS/1.3 | 2008 年, ISOC 发布修订版 |
目前, 当前广泛使用的版本是 SSL3.0 和 TLS1.0。主流浏览器都已经实现了 TLS 1.2 的支持。
HTTPS 比 HTTP 慢?慢多少?
HTTPS 在使用 SSL 时它的速度会变慢。
SSL 的慢分两种。一种是指通信慢。另一种是指由于大量消耗 CPU 及内存等资源, 导致处理速度变慢。 和使用 HTTP 相比, 网络负载可能会变慢 2 到 100 倍。除去和 TCP 连接、发送 HTTP 请求 • 响应以外, 还必须进行 SSL 通信, 因此整体上处理通信量不可避免会增加。 另一点是 SSL 必须进行加密处理。在服务器和客户端都需要进行 加密和解密的运算处理。因此从结果上讲, 比起 HTTP 会更多地 消耗服务器和客户端的硬件资源, 导致负载增强。
针对速度变慢这一问题, 并没有根本性的解决方案, 我们会使用 SSL 加速器这种(专用服务器)硬件来改善该问题。该硬件为 SSL 通信专用硬件, 相对软件来讲, 能够提高数倍 SSL 的计算速 度。仅在 SSL 处理时发挥 SSL 加速器的功效, 以分担负载。
Reference
- 阮一峰 SSL/TLS 协议
- 图解 SSL/TLS 协议
- 鸽子传信解释 HTTPS
- 图解 HTTPS:Charles 捕获 HTTPS 的原理
- 看完还不懂 HTTPS 我直播**
- 免费申请 HTTPS 证书, 开启全站 HTTPS
2. DNS 协议(会话层)
DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于 TCP/IP 网络,它所提供的服务是用来将主机名和域名转换为 IP 地址的工作。
2.1 DNS 查找过程
域名 -> 检查浏览器缓存 -> 检查系统 Hosts 文件 DNS 缓存 -> 检查路由器缓存 -> 检查 ISP(互联网服务提供商)DNS 缓存 -> 根域名服务器 -> 顶级域名服务器 -> 权威域名服务器
以步骤中当检查到 ip 时将终止向后查询的步骤并保存结果至缓存。
2.2 递归查询和迭代查询
递归查询:如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户的身份,向其他根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步的查询。
迭代查询:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的 IP 地址,要么告诉本地域名服务器:你下一步应当向哪一个域名服务器进行查询。然后让本地域名服务器进行后续的查询,而不是替本地域名服务器进行后续的查询。
由此可见,客户端到 Local DNS 服务器,Local DNS 与上级 DNS 服务器之间属于递归查询;DNS 服务器与根 DNS 服务器之前属于迭代查询。
2.3 HTTPDNS
HTTPDNS 使用 HTTP 与 DNS 服务器交互,代替传统的基于 UDP 的 DNS 协议,域名解析请求直接发送到 HTTPDNS 服务端,从而绕过运营商的 Local DNS,防止 DNS 劫持。
Reference
DNS 原理入门 - 阮一峰
HttpDns 原理是什么
3. TCP 协议(传输层)
TCP 协议 是以太网协议和 IP 协议的上层协议, 也是应用层协议的下层协议。
TCP 协议 全称是传输控制协议是一种 ** 面向连接的、可靠的、基于字节流的传输层通信协议 **, 由 IETF 的 RFC 793 定义。TCP 是面向连接的、可靠的流协议。
3.1 OSI 网络模型(OSI 七层网络模型、TCP/IP 四层概念模型、五层协议的体系结构)
OSI 七层网络模型 | TCP/IP 四层概念模型 | 五层协议的体系结构 | 对应网络协议 |
---|---|---|---|
7. 应用层 | 4. 应用层 | 5.应用层 | HTTP、TFP、NFS、TFTP、WAIS、SMTP |
6. 表示层 | ^ | ^ | Telnet、Rlogin、SNMP、Gopher |
5. 会话层 | ^ | ^ | DNS、SMTP |
4. 传输层 | 3. 传输层 | 4. 传输层 | TCP、UDP |
3. 网络层 | 2. 网际层 IP | 3. 网络层 | IP、ICMP、ARP、RARP、AKP、UUCP |
2. 数据链路层 | 1. 网络接口层 | 2. 数据链路层 | Ethernet、FDDI、Arpanet、PDN、SLIP、PPP |
1. 物理层 | ^ | 1. 物理层 | IEEE 802.1A、IEEE 802 - IEEE 802.11 |
- Tips:
- ^ 表示向上合并单元格
- TCP/IP 是广泛使用的模型, 分四层。osi 模型是理论下的国际标准模型, 分七层。五层模型是综合了 OSI 和 TCP/IP 模型得到的五层模型, 为方便学习计算机网络原理而采用。
3.2 TCP 数据包大小
以太网数据包大小为 1522 字节, 其中 22 字节是头信息, 1500 字节是负载。
IP 数据包在以太网数据包负载里面, IP 数据包头信息也需要最少 20 字节, 负载最多为 1460 字节。
TCP 数据包在 IP 数据包中, TCP 头信息也有额外的头信息, 所有 TCP 负载实际在 1400 字节左右。
3.3 TCP 报文首部字段注析
| 字段 | 字段名 | 缩写 | 作用 |
| 应答响应 | Acknowledgment | ACK | 表示确认序号有效。确认应答的字段有效。TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1。 |
| 同步 | Synchronize | SYN | 用来发起一个连接,建立连接。SYN 为 1 表示希望建立连接。 |
| 结束 | Finish | FIN | 结束会话,关闭连接。表示发送方完成任务,今后不会有数据发送,希望断开连接。 |
| 初始序列号 | Initial Sequence Number | ISN | 一个随机数,在三次握手的过程当中,使用 seq 字段交换双方的 ISN。 |
| 序列号 | Sequence Number | seq | 指的是本报文段第一个字节的序列号。 |
| 确认应答号 | Acknowledgement Number | ack(ACK number) | 用来告知对方下一个期望接收的序列号,小于 ack 的所有字节已经全部收到。 |
3.4 三次握手
- 最初客户端和服务端都处于 CLOSED(关闭) 状态。
- TCP 服务器进程先创建传输控制块 TCB, 时刻准备接受客户进程的连接请求, 此时服务器就进入了 LISTEN(监听)状态;
- TCP 客户进程也是先创建传输控制块 TCB, 然后向服务器发出连接请求报文, 这是报文首部中的同部位 SYN=1, 同时选择一个初始序列号 seq=x , 此时, TCP 客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP 规定, SYN 报文段(SYN=1 的报文段)不能携带数据, 但需要消耗掉一个序号。
- TCP 服务器收到请求报文后, 如果同意连接, 则发出确认报文。确认报文中应该 ACK=1, SYN=1, 确认号是 ack=x+1, 同时也要为自己初始化一个序列号 seq=y, 此时, TCP 服务器进程进入了 SYN-RCVD(同步收到)状态。这个报文也不能携带数据, 但是同样要消耗一个序号。
- TCP 客户进程收到确认后, 需要向服务器发送确认消息。确认报文的 ACK=1, ack=y+1, 自己的序列号 seq=x+1, 此时, TCP 连接建立, 客户端进入 ESTABLISHED(已建立连接)状态。TCP 规定, ACK 报文段可以携带数据, 如果不携带数据不消耗序号。
- 当服务器收到客户端的确认后也进入 ESTABLISHED(已建立连接)状态, 此后双方就可以开始通信了。
3.5 四次挥手
- 数据传输完毕后, 双方都可主动释放连接。
- 客户端进程发出连接释放报文, 并且停止发送数据。释放数据报文首部, FIN=1, 其序列号为 seq=u(等于前面已经传送过来的数据的最后一个字节的序号加 1), 此时, 客户端进入 FIN-WAIT-1(终止等待 1)状态。 TCP 规定, FIN 报文段即使不携带数据, 也要消耗一个序号。
- 服务器收到连接释放报文, 发出确认报文, ACK=1, ack=u+1, 并且带上自己的序列号 seq=v, 此时, 服务端就进入了 CLOSE-WAIT(关闭等待)状态。TCP 服务器通知高层的应用进程, 客户端向服务器的方向就释放了, 这时候处于半关闭状态, 即客户端已经没有数据要发送了, 但是服务器若发送数据, 客户端依然要接受。这个状态还要持续一段时间, 也就是整个 CLOSE-WAIT 状态持续的时间。
- 客户端收到服务器的确认请求后, 此时, 客户端就进入 FIN-WAIT-2(终止等待 2)状态, 等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后, 就向客户端发送连接释放报文, FIN=1, ack=u+1, 由于在半关闭状态, 服务器很可能又发送了一些数据, 假定此时的序列号为 seq=w, 此时, 服务器就进入了 LAST-ACK(最后确认)状态, 等待客户端的确认。
- 客户端收到服务器的连接释放报文后, 必须发出确认, ACK=1, ack=w+1, 而自己的序列号是 seq=u+1, 此时, 客户端就进入了 TIME-WAIT(时间等待)状态。注意此时 TCP 连接还没有释放, 必须经过 2MSL(最长报文段寿命)的时间后, 当客户端撤销相应的 TCB 后, 才进入 CLOSED 状态。
- 服务器只要收到了客户端发出的确认, 立即进入 CLOSED 状态。同样, 撤销 TCB 后, 就结束了这次的 TCP 连接。服务器结束 TCP 连接的时间要比客户端早一些。
等待 2MSL 的意义
2MSL(Maximum Segment Lifetime,报文最大生存时间)
- 保证客户端发送的最后一个 ACK 报文段能够到达服务端。
这个 ACK 报文段有可能丢失,使得处于 LAST-ACK 状态的 B 收不到对已发送的 FIN+ACK 报文段的确认,服务端会进行超时重传 FIN+ACK 报文段,而客户端能在 2MSL 时间内收到这个重传的 FIN+ACK 报文段,接着客户端重传一次确认,重新启动 2MSL 计时器,最后客户端和服务端都进入到 CLOSED 状态,若客户端在 TIME-WAIT 状态不等待一段时间,而是发送完 ACK 报文段后立即释放连接,则无法收到服务端重传的 FIN+ACK 报文段,所以不会再发送一次确认报文段,则服务端无法正常进入到 CLOSED 状态。 - 防止已失效的连接请求报文段出现在本连接中。
客户端在发送完最后一个 ACK 报文段后,再经过 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
为什么是四次挥手而不是三次?
当服务器执行第二次挥手之后, 此时证明客户端不会再向服务端请求任何数据, 但是服务端可能还正在给客户端发送数据(可能是客户端上一次请求的资源还没有发送完毕),所以此时服务端会等待把之前未传输完的数据传输完毕之后再发送关闭请求。
3.6 重传机制
1、超时重传
当发送数据包在超时重传时间 RTO(Retransmission Timeout)内没有收到响应的 ack,认为这个数据包丢失,就会重新发送数据包。
2、快速重传
当发送方连续收到 3 次相同的 ack,这个时候即使超时定时器还没有超时,也会进行启动重传。
重传数据包处理:
当触发重传机制时,应该只重传丢失的报文还是应该重传丢失的报文以及序号在后面的报文(前面已发送的报文)?
为了解决不知道该重传哪些报文的问题,于是有了 SACK 方法。
- 选择性确认 SACK( Selective Acknowledgment )
在 TCP 头部中加入一个 SACK 字段,用来记录已收到的报文序号,知道了已收到的序号就可以做到只重传丢失的报文。
3.7 滑动窗口
滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。TCP 的滑动窗口解决了端到端的流量控制问题,允许接受方对传输进行限制,直到它拥有足够的缓冲空间来容纳更多的数据。
3.8 拥塞控制
拥塞控制协议是作用于网络的,防止过多的数据注入到网络中,避免出现网络负载过大的情况。
拥塞控制算法:
- 慢开始
发送方维持一个叫做拥塞窗口 cwnd(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化,一开始先发送少量的数据而后取决于网络堵塞情况根据算法动态调整拥塞窗口大小。慢开始的发包个数是指数性的增长。 - 拥塞避免
当 cwnd 大于等于慢启动门限 ssthresh(slow start threshold)时,将使用拥塞避免算法。拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。
拥塞避免的发包个数是线性的增长。 - 拥塞发生
当网络进入拥塞状况时,会出现丢包现象,这时就需要对丢失的数据包进行重传(超时重传或快速重传)。当触发超时重传时会使用拥塞发生算法。
拥塞发生算法会将 ssthresh 设为 cwnd/2, cwnd 重置为 1,随后重新开始慢启动。 - 快速恢复
当触发快速重传机制时,TCP 认为这种情况不严重,会将 cwnd 设为 cwnd/2, ssthresh 设为 cwnd , 随后进入快速恢复算法。
快速恢复算法会将 cwnd 设为 ssthresh + 3 (3 个 ASK 报文) , 然后重传丢失的数据包,如果再收到重复的 ACK,那么 cwnd 增加 1, 如果收到新数据的 ACK 后会将 cwnd 设置 为 ssthresh,接着就进入了拥塞避免算法。
3.9 快速打开(TFO)
开启 TFO 功能
- 正常进行三次握手建立连接,在第一次握手的时候添加 Fast Open 选项,请求打开 TFO。
- 支持 TFO 功能的服务器生成 Cookie,并通过第二次握手中的 Fast Open 字段返回给客户端。
- 客户端收到 SYN-ACK 后,缓存 Fast Open 选项中的 Cookie。
** 使用 TFO 功能 ** - 客户端发起第一次握手包,数据包中包含 Cookie、SYN 和数据(例如 HTTP 请求)。
- 服务器接收到 Cookie 并进行校验: 如果 Cookie 有效,服务器将在 SYN-ACK 数据包中对 SYN 和数据进行确认,随后将响应数据发送给客户端。如果 Cookie 无效,服务器将丢弃数据包中的数据,随后只发送 SYN-ACK 数据包进行握手确认。
- 客户端发送 ACK 确认服务器返回的 SYN 以及数据, 如果客户端在初始化 SYN 数据包的数据没有被服务器端确认,客户端将重发该数据。
3.10 TCP keep-alive 机制
通讯双方建立起连接,如果通讯双方没有发起释放连接,连接通道将会一直打开,这时如果有一方出现了掉电、死机、异常重启等意外情况,连接另一方不知道发生异常将会一直保持连接造成资源浪费。为了解决这个问题,在传输层可以利用 TCP 的保活报文来实现。
在收到对端确认报文时设置保活定时器,时间通常小于两小时, 每隔 75 秒发送一次。如果在保活时长内未收到对端数据,就会发送探测报文,如果连续发送 10 个探测报文仍无反应,则可认为对端出现故障,接着就关闭连接。
TCP Keepalive 虽不是标准规范,但操作系统一旦实现,默认情况下须为关闭,可以被上层应用开启和关闭。
3.11 安全
可靠性
- 应答机制
- 重发机制
- 滑动窗口
- 拥塞控制
半连接队列
当客户端发送 SYN 到服务端,服务端收到以后回复 ACK 和 SYN,状态由 LISTEN 变为 SYN_RCVD,此时这个连接就被推入了 SYN 队列,也就是半连接队列。
全连接队列
当客户端返回 ACK, 服务端接收后,三次握手完成。这个时候连接等待被具体的应用取走,在被取走之前,它会被推入另外一个 TCP 维护的队列,也就是全连接队列(Accept Queue)。
SYN Flood 攻击
SYN Flood 是一种拒绝服务(DDoS)攻击,其目的是通过消耗所有可用的服务器资源使服务器不可用于合法流量。通过重复发送初始连接请求(SYN)数据包,攻击者能够压倒目标服务器机器上的所有可用端口,导致目标设备根本不响应合法流量。
攻击者伪造大量的不存在的 IP 向服务器端发起连接请求,占满服务器端的半连接队列导致服务器无法响应后续的正常请求。
如何应对 SYN Flood 攻击?
- 增加半连接队列的容量,回收最早的半连接。
- 限制 SYN 并发数量、超时时间.
- 启用 SYN cookie。
3.12 TCP 与 UDP 的区别
| 特性 | TCP | UDP |
| 可靠性 | 可靠的 | 不可靠 |
| 连接性 | 面向连接 | 无连接 |
| 报文 | 面向字节流 | 面向报文 |
| 效率 | 传输效率低 | 传输效率高 |
| 双工性 | 全双工 | 一对一、一对多、多对多 |
| 流量控制 | 滑动窗口| 无 |
| 拥塞控制 | 慢开始、拥塞避免、快重传、快恢复 | 无 |
| 传输速度 | 慢 | 块 |
| 应用场景 | 适用于要求可靠传输的应用,例如文件传输 | 适用于实时应用(IP 电话、视频会议、直播等) |
Reference
- TCP 协议详解之 TCP
- TCP 协议简介
- 两张动图-彻底明白 TCP 的三次握手与四次挥手
- Java 技术干货-知乎专栏
- TCP Fast Open
- 聊聊 TCP 中的 KeepAlive 机制
- 图解 SSL 从回车到握手
- 你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了
- TCP 流量控制、拥塞控制
- 一篇文章告诉你,TLS 1.3 如何用性能为 HTTPS 正名
4. IP 协议(网络层)
IP 协议为上层协议提供无状态、无连接、不可靠的服务。
4.1 特性
-
无状态
- 无状态是指 IP 通信双方不同步传输数据的状态信息,所有 IP 数据报的发送、传输、接受都是相互独立、没有上下文关系的。这种服务优点在于简单、高效。最大的缺点是无法处理乱序和重复的 IP 数据报,确保 IP 数据报完整的工作只能交给上层协议来完成。
-
无连接
- 无连接是指 IP 通信双方都不长久地维持对方的任何信息。上层协议每次发送数据的时候,都需要明确指出对方的 IP 地址。
-
不可靠
- 不可靠是指 IP 协议不能保证 IP 数据报准确到达接收端,它指承诺尽最大努力交付。IP 模块一旦检测到数据报发送失败,就通知上层协议,而不会试图重传。
4.2 IP 报头
Version(版本号):标识 IP 协议的版本,目前 V4 版本地址已经枯竭,V6 慢慢成为主流。
Header Length(头部长度):默认为 20 字节,最大为 60 字节。
Differentiated Services Field (服务区分符):用于为不同的 IP 数据包定义不同的服务质量,一般应用在 QoS 技术中。
Total Length (总长度):标识 IP 头部加上上层数据的数据包大小,IP 包总长度最大为 65535 个字节。
Identification (标识符):用来实现 IP 分片的重组,标识分片属于哪个进程,不同进程通过不同 ID 区分。
Flags(标志符):用来确认是否还有 IP 分片或是否能执行分片。
Fragment offset (分片偏移量):用于标识 IP 分片的位置,实现 IP 分片的重组。
Time to live (生存时间):标识 IP 数据包还能生存多久,根据操作系统不同,TTL 默认值不同,每经过一个三层设备如路由器的处理,则 TTL 减去 1,当 TTL=0 时,则此数据包被丢弃。
Protocol (协议号):标识 IP 协议上层应用。当上层协议为 ICMP 时,协议号为 1,TCP 协议号为 6,UDP 的协议号为 17。
Header checksum (头部校验):用于检验 IP 数据包是否完整或被修改,若校验失败则丢弃数据包。
Source(源 IP 地址):标识发送者 IP 地址,占用 32bit。
Destination (目的 IP 地址):标识接收者 IP 地址,占用 32bit。
4.3 IP 地址类型
- 公有地址
- 公有地址(Public address)由 Inter NIC(Internet Network Information Center 因特网信息中心)负责。这些 IP 地址分配给注册并向 Inter NIC 提出申请的组织机构。通过它直接访问因特网。
- 私有地址
- 私有地址(Private address)属于非注册地址,专门为组织机构内部使用。以下列出留用的内部私有地址 A 类 10.0.0.0--10.255.255.255B 类 172.16.0.0--172.31.255.255C 类 192.168.0.0--192.168.255.255
4.4 IP 地址分类
IP 地址分类成了 5 种类型,分别是 A 类、B 类、C 类、D 类、E 类。
Reference
- IP 协议详解
- 图解 IP 协议