大家好呀,我是小菜~
本文主要介绍
网络
如有需要,可以参考! 如有帮助,不忘 点赞 ❥ 微信公众号已开启,菜农曰,没关注的同学们记得关注哦!
我们先思考一个问题, 什么是网络?
网络 这个东西在我们的生活中可谓是如影随形! 但是网络这个词又太过泛化, 我们最直观的认识就是打开浏览器, 输入某个地址, 然后给我们响应出页面. 既然有了网络, 为了通信正常, 就出现了各种各样的 网络协议, 协议的主要作用就是 约束, 只有双方互相遵守协议, 那么通信才能正常的走下去 !
那么我们平时接触最多的是什么协议呢?
有小伙伴就抢答了, Http 协议 ! 又有的小伙伴说是 Https 协议
两个回答都是正确的, 正常来说可以统称为 Http 协议, 而带上锁的就是 Https 协议, 那么这两个协议有什么不同呢? 这就是我们这篇要讲到的内容
HTTP
HTTP ( Hypertext Transfer Protocol ), 超文本传输协议
一. 历程回顾
看到这个词莫名有种亲切感, 因为它贴近我们的生活, 我们可以回顾它的成长历程
- 1991 年
它诞生了, 当时名为 HTTP 0.9
, 这个版本极其简单, 只有一个 Get 命令, 也就是只能获取资源
- 1996 年
经过 5 年的时间, 孵化了 1.0 这个版本, 也就是 HTTP 1.0
, 这个版本有了很大的升级, 以至于现在大多数人认为 1.0 是它的初始版本, 而不知道 0.9 这个版本的存在. 这个版本相对成熟, 任何格式的内容都可以发送, 这使得互联网不仅可以传输文字,还能传输图像、视频、二进制文件, 这为互联网的大发展奠定了基础 !
- 1999 年
HTTP 1.1
版本随之发布. Http 1.0 相对成熟, 但有缺陷. 而 1.1 版本在继承了HTTP 1.0优点的基础上,也克服了HTTP 1.0的性能问题
- 2015 年
HTTP/2
出现了, 这次的发版不再有 .x 的版本形式, 也说明了这次发版的自信与实力. 但目前这个版本还比较新, 还没达到普及的程度
二. HTTP1.0
HTTP 协议有什么特点?
最基本的一个特点就是 一来一回
, 它是在服务端收到请求后才会做出回应. 也就是说客户端在获取资源的时候会发起一个 TCP 连接, 在连接上发一个 HTTP Request 到服务器, 然后服务器才会返回一个 HTTP Response 做出响应, 那这样就会每来一个请求, 就会打开一个连接
那么问题来了, 这样子会有什么问题 ? 答案是肯定的, 明显的问题如下:
- 性能问题
连接的建立和关闭都是耗时操作, 而我们现在打开一个网页, 加载了几十个资源, 那如果没请求一次就要开一个 TCP 连接, 那是非常耗时的. 有小伙伴就说了, 现在都流行并发了, 可以开多个连接, 并发发送呀 ! 是的, 的确可以并发发送请求, 但是我们还需要明白一点, 连接数不是无限的 !
- 推送问题
我们在上面说到了服务端是被动响应的, 也就是服务端没有办法在客户端没有请求的情况下主动向客户端推送消息. 当然这个问题放到现在也不算什么了, 已经有了很多解决方案!
我们先来认识 HTTP1.0 这个版本, 0.9 实在有太多让人诟病的地方, 因此不算完美的它出现了改进版也就是 1.0 版本. 这个版本给我们带了两个特性
1. Keep-Alive 机制
众所周知, 性能问题是很严重的问题, 频繁的连接建立断开严重损耗了性能. 我们完全可以想象获取每个资源都需要一个连接, 而且一个连接用完就立刻关闭, 这多少有点奢侈挥霍了 ~
因此为了解决这个问题, HTTP 1.0 带来了 Keep-Alive 机制, 它可以实现 TCP 连接的复用
这个机制如何体现呢?
客户端只需要在请求的头部加上一个标识: Connection: Keep-Alive
, 当服务端收到来自客户端的属性后, 看到该请求的头部携带了 Keep-Alive 标识, 便不会在请求处理结束后关闭该连接, 同时在 HTTP 的响应头中也会加上该字段, 告诉客户端这个请求没有关闭
但是我们需要知道 TCP 连接数是有限的, 如果每个资源请求过来都想保持连接存活, 这也是一件不现实的事情, 因此, 服务端会有一个 keep-Alive timeout
参数, 过一段时间之后, 如果该连接上没有新的请求进来, 连接就会关闭
2. Content-Length 属性
这个属性与上面那个机制息息相关, 之前的处理方式都是一个连接一个请求响应一类资源, 当资源返回后客户端就知道这个连接直接关闭了. 当有了 Keep-Alive 机制后, 客户端就不知道这个请求什么时候关闭了, 一个请求处理完了,连接也不关闭,那么客户端怎么知道连接处理结束了呢?或者说,客户端怎么知道接收回来的数据包是完整的呢?
因此就有了 Content-Length 属性, 这个字段可以告诉客户端 HTTP Response 的 Body 有多少个字节, 那么客户端接收到多少个字节之后就知道响应成功了
三. HTTP 1.1
每一次的版本更新都是为了填上个版本留下的坑
上图是 1.0 版本带来的解决问题, 可以说解决性能问题, 连接复用是很有必要的. 因此到了 HTTP1.1 连接复用就变成了一个默认属性, 即使不加上 Connection: Keep-Alive 属性, 服务器也会在请求处理完毕之后不关闭连接. 除非显式的加上 Connection: close
为了让客户端判断一个请求的响应是否成功, 1.0 也煞费苦心引入了 Content-Length 属性, 那么问题就来了, 如果服务端返回的内容过长且复杂, 那么 Content-Length 的计算是不是也变的尤为耗时? 而且客户端也为了比对, 也需要计算 Content-Length,
1. Chunk 机制
因此 HTTP 1.1 为了解决这个问题, 引入了 Chunk
机制 (Http Streaming), 也就是服务端会在响应头加上 Transfer-Encoding: chunked
属性, 这样是为了告诉客户端, 响应的 Body 是分成了一块块, 每块之间都有分割符, 所有块的结尾也有个特殊标记, 因此这样客户端也可以很快的判断出响应的末尾
2. Pipeline 与 Head-of-line Blocking
当我们引入了连接复用后, 感觉还是很慢是怎么回事?
因为这个时候还存在一个问题, 请求是串行的, 客户端发送一个请求, 收到响应后才会继续发送下一个请求
擅长写代码的我们都习惯一言不合就多线程, 一个线程不够就两个, 就是要提高并发度, 只有并发度上来了, 性能才能提升 ! 当然这个理论上的定义, 具体实现在开发中不一定多线程就是好
HTTP 1.1 也因此引入了 Piepline 机制
, 在同一个 TCP 连接上面, 可以在一个请求发出去之后, 响应没有回来之前, 就可以再发送下一个请求, 这样提高了并发度, 也提高了性能 ~
但凡事都有双面性, Piepline 机制
也有个致命的问题, 那就是 Head-of-Line Blocking
. 大白话翻译过来就是 队头阻塞
客户端发送的请求是 1, 2, 3, 三次请求在服务端上可以并发处理. 但是客户端也有自己的脾气, 接受响应的顺序也必须是 1, 2, 3, 一旦请求 1 发生了延迟, 则请求2, 3也会被阻塞. 因此为了避免 Piepline 带来的副作用, 很多浏览器默认就是把该机制关闭了
那么既然 Piepline 这条路行不通, 有没有其他的妙招? 当然 ! 你永远可以相信前辈的奇思妙想 ~
3. 脑洞风暴
在浏览器中, 对于同一个域名, 只能开 6~8 个连接, 但一个网页需要发送几十个HTTP请求, 那么 6~8 个连接是远远不够用的
1. Spriting 技术
这种技术是针对小图片的, 既然限制了连接数, 那么就可以很多歌小图片拼成一个大图片, 到了浏览器后, 再通过 JS 或 CSS , 从大图中截取一小块显示, 而这个可以将之前需要发很多个请求的问题, 变成只发一个请求就可以解决了
2. Inlining 内联
内联是另外一种针对图片加载的技术, 它直接将图片的 Base64 插入 CSS 文件中, 就避免了加载
3. JS 拼接
把大量小的 JS 文件合并成一个文件并压缩, 让浏览器在一次请求中可以直接加载完
4. 请求的分片技术
既然一个域名只能建立 6~8次连接, 那岂不是多整几个域名, 就可以提升连接数了, 这就相当于绕开了浏览器的限制.
4. 断点续传
HTTP 1.1 还有个好用的功能就是 断点续传
, 当客户端从服务器下载文件的时候, 如果下载到一半连接中断了, 再新建连接之后客户端还是可以从上次断的地方继续下载.
这是因为客户端在下载的时候, 一边下载一边记录下载的数据量大小, 一旦连接中断了, 就可以在请求的头部加上 Range: first offset-last offset
字段, 指定从某个 offset 下载
但这种特性只适用于断点下载, 断点上传需要自己实现
四. HTTP/2
每一次的版本更新都是为了填上个版本留下的坑
到目前为止我们从HTTP0.9
认识到了HTTP1.1
, 在 HTTP/2 出现之前, 1.1 也陪伴了我们长达六年
在 HTTP1.1 的时候由于 Piepline 导致的队头阻塞问题, 而大家集思广益, 出现了各种各样的解决方案. 但有没有发现这些解决方案都是从 应用层面 去解决的, 没有普适性, 因此想要通用, 还是得从 协议 层面解决. 因此基于这个方向, 出现了 HTTP/2
, 大家也可以发现这个版本并没有称之为 2.0, 可能是因为工作组认为该协议已经足够完善, 不会再有小版本进行修复, 如果有的话也就是 HTTP/3
的事情了.
1. 二进制分帧
队头阻塞的问题是一个难题, 哪怕是 HTTP/2 也没有解决
二进制分帧
是 HTTP/2 为了解决 队头阻塞 问题设计的核心特性, HTTP 1.1本身是明文的字符格式,而二进制分帧,是指在把这个字符格式的报文给TCP之前转换成二进制,并且分成多个帧(多个数据块)来发送。
图源网, 侵删
在 HTTP/2 连接数据发送的过程中, 请求和响应都是被打散后分成多个帧乱序地发出去的, 请求和响应都需要重新组装起来, 同时请求和响应还要一一配对, 那么问题又随之而来了.
组装和配对如何实现 ?
其实这边解决的原理也很简单, 每个请求和响应实际上组成了一个逻辑上的 流, 为每条流分配一个流 ID
, 把这个ID作为每个流的标签.
这种实现方式有没有让你想到什么? 是不是类似我们上面说到的数据包编号 ? 没错, 所以说 HTTP/2 并没有完全解决 队头阻塞 问题, 而是把 队头阻塞 问题从HTTP Request粒度细化到了“帧”粒度
, 尽管没有解决, 但是基于 帧 的粒度可以降低队头阻塞发生的可能性, 同样提高了性能 !
图源网, 侵删
为什么 HTTP/2 还是没能解决队头阻塞的问题呢?
这个问题我们需要从源头分析, 这里就直接给出结论了: 只要用 TCP 协议,就绕不开“队头阻塞”问题,因为 TCP 协议是先进先出的!
2. 头部压缩
除了二进制分帧,HTTP/2 另外一个提升效率的方法就是使用头部压缩
。
在HTTP 1.1里,对于报文的报文体,已经有相应的压缩,尤其对于图片,本来就是压缩过的. 但对于报文的头部,一直没有做过压缩。
在互联网野蛮生长的时代, 应用场景越来越复杂, 很多时候都会将参数放到请求头中, 这就会导致报文的头部变得很大, 这个时候对头部做压缩就变得很有必要. 因此 HTTP/2 专门设计了一个 HPACK 协议和对应的算法. 这种方式同样也能够过提高传输的速度
六. HTTPS
互联网充满了危险, 而有危险的地方就需要安全的保证, 因此它来了 - HTTPS
1. SSL/TLS
在正式介绍 HTTPS 之前, 我们很有必要先认识下 SSL/TLS
- SSL: Secure Sockets Layer, 安全套接层
- TLS: Transport Layer Security, 传输层安全协议
小菜曾经认为这两个概念是在 HTTPS 提出之后才引入的, 现在想想难免有些 荒唐, 不知道有多少伙计跟小菜想的一样
既然引入时间不详, 那就很有必要做一个历史回顾了
SSL/TLS 出现的时间很早, 有多早? 几乎和互联网历史一样长
- 1994年: 网景公司设计了 SSL 1.0
- 1995年: 网景公司发布了 SSL 2.0, 但很快发现存在严重漏洞
- 1996年: SSL 3.0 发布, 得到大规模应用
- 1999年: 互联网标准化组织 IETF 对 SSL 进行标准化, 发布了 TLS 1.0
- 2006年和2008年: TLS 进行了两次升级, 分别为 TLS 1.1 和TLS 1.2
那么在网路层中 , SSL/TLS 是处于什么位置呢? 我们可以看下图:
它是位于应用层与传输层之间, 不仅可以支撑 HTTP 协议, 还可以支持 FTP, IMTP 等其他各种协议
SSL/TLS 主要是用于保证安全传输的, 如何保证安全传输? 那就需要使用加密了, 我们先来了解几种加密模式
对称加密
对称加密实现很简单, 就是双方共同持有一个密钥, 来往消息都采用同一个密钥加解密即可
但是问题就来了, 客户端与服务端双方还未进行约定之前, 如何通知双方密钥是什么?
密钥A 通过 密钥B 加密, 密钥B 再通过 密钥C 加密? 这岂不是套娃吗 !
因此这种加密方式不适用 !
非对称加密
客户端为自己准备一对公私钥 ( PubA,PriA ), 服务器为自己准备一对公私钥 ( PubB,PriB ). 公私钥有个关键特性:公钥PubA是通过私钥PriA 计算出来的,但反过来不行,不能根据PubA推算出PriA
通过这种方式双方只需要将自己的公钥公开出去就行, 自己保留就行.
而 签名 的目的就相当于盖章, 说明这段消息是你发送出来的, 具有不可抵赖性质.
这种方式挺合理的, 但是同样会遇到一个问题, 那就是公钥如何安全传输?
也就经典的中间人攻击, 在传输公钥的时候中间人可以做拦截, 将双方的公钥换成中间人的公钥, 那么接下来的通信相当于都在中间的掌控之中
那么就需要想个办法证明服务器收到的公钥, 的确就是客户端发出的, 中间没有人可以篡改这个公钥,反过来也是一样.
数字证书与证书认证中心
为了解决这个问题, 我们急需一个公证处 CA , 在公证处保管着公钥, 凡是在公证处有保存过公钥的, 公证处都会颁发一个数字证书 ( Certficate ), 这个证书就相当于是一张身份证. 之后的传输双方只需要传输证书, 然后拿着证书去公证处核定是否真实
那么这个时候就又会出现一个问题,CA是假的怎么办
, 中间人也有可能充当 CA 这个角色, 双方高高兴兴的通信半天, 到头来发现是跟中间人通信, 这不得一口老血吐出来
而这个时候就需要给 CA 颁发证书
, 那么 CA 的证书又该由谁来颁发呢, 那就是 CA 的上一级 CA, 这就是证书信任链
这边涉及到两个过程:
- 验证过程
- 客户端要验证服务器的合法性,需要拿着服务器的证书C3,到CA2处去验证
- 客户端要验证CA2的合法性,需要拿着CA2的证书C2,到CA1处去验证
- 客户端要验证CA1的合法性,需要拿着CA1的证书C1,到CA0处去验证
那么其中 CA0 如何认证呢 ? 对于它我们只能无条件信任. 他就说传说的 根证书
, Root CA 机构都是一些世界上公认的机构,
- 颁发过程
颁发过程也是类似的, 上一级CA给下一级CA颁发证书。从根CA(CA0)开始,CA0给CA1颁发证书,CA1给CA2颁发证书,CA2给应用服务器颁发证书
在我们了解了以上概念后, 再来看 SSL/TLS 就不难了,
在建立TCP连接之后、数据发送之前,SSL/TLS协议会通过四次握手, 来协商出客户端和服务器之间的对称加密密钥, 然后之后的连接都会带上这个密钥
2. HTTPS
那么什么是 HTTPS, 现在我们只要记住一个公式就可以了解了, 那就是 : HTTPS = HTTP SSL/TLS
我们到这里已经掌握了 HTTP, 也掌握了 SSL/TLS, 也就间接掌握了 HTTPS~
因此, 到了 HTTPS 这里传输过程就分成了三个阶段:
- 阶段一: TCP 连接的建立
- 阶段二: SSL/TLS 四次握手协商出对称加密的密钥
- 阶段三: 基于密钥, 在TCP连接上对所有的 HTTP Req/Res 进行加解密传输
有些伙计就提问了, TCP 连接建立就已经很耗时了, 现在还要加上 SSL/TLS 的握手, 那 HTTPS 的速度岂不是很慢?
其实不然, 在传输过程中阶段一和阶段二只在连接建立的时候做一次, 之后只要连接不关闭, 每个请求都只需要经过阶段三, 因此性能并没有太大影响 !
五. TCP/UDP
既然说到了 HTTP, 那么 TCP 和 UDP 是绕不开的, 我们一块讲讲 ~
为什么绕不开呢, HTTP 和 TCP 之间有什么关系?
HTTP是要基于TCP连接基础上的,简单的说,TCP就是单纯建立连接,不涉及任何我们需要请求的实际数据,简单的传输。HTTP是用来收发数据,即实际应用上来的。
我们可以总结三个点:
- TCP是
底层通讯协议
,定义的是数据传输和连接方式的规范 - HTTP是
应用层协议
,定义的是传输数据的内容的规范 - HTTP协议中的数据是利用TCP协议传输的,所以支持HTTP也就一定支持TCP
而要讲 TCP 自然会带上 UDP 做比较 , 这就是一对亲兄弟, 我们下来分别从几个特性来介绍 TCP 和 UDP 的区别
可靠性
要问你对 TCP 和 UDP 的认识, 大多数人都会顺口就来, 一个是可靠连接, 一个是不可靠连接 ! 那小菜就要提出几个问题了
哪个是可靠连接, 哪个是不可靠连接
可靠连接的定义是什么
可靠连接是如何保证可靠的
第一个问题很简单, 绝大部分同学都可以轻松回答: TCP 是可靠连接, UDP 是不可靠连接.
到了第二个问题可能就有点犯怵了
"可靠? 就是值得信赖是吗?"
值得信赖是对的, 但是信赖太过泛化了, 为什么值得信赖, 有哪些值得信赖的地方?
这里给出三点定义:
数据包不丢
数据包不重
时序不乱
只有符合以上三点, 才算是可靠连接, 而 TCP 正好符合 ~
那么继续我们的第三个问题, 如何保证可靠 ? 小菜这里也就不卖关子了, 咱们接着往下看
首先是 数据包不丢
1. 解决不丢问题 : ACK 重发
网络中丢包是一种很正常的现象, 丢了怎么办? 重发呗 ! 就是这么简单暴力, 服务器每次收到一个包, 就会对客户端进行确认, 反馈给客户端收到包的信号, 也就是 ACK 信号, 而如果客户端在指定的时间内没有收到 ACK 确认信号, 就会再次重发数据.
但是 ACK 信号是收到一个包, 就发送一个确认吗? 不是的 ! 这种效率实在太低了, 那么解决方法就是: 客户端会对发送的每个数据包进行编号, 编号由小到大依次递增, 那么只有基于编号就能确定好顺序
就好比如服务端收到了包 1, 2, 3. 那么它只需要回复客户端 ACK=3
, 意思是所有小于或等于 3 的数据包都已经收到了, 接着继续收到包 4, 5, 6. 而这时就需要回复 ACK=7
, 意思就是所有小于或等于7的数据包都已经收到了
2. 解决不重问题 : ACK 判定
上面说到客户端在指定的时间内没有收到 ACK 确认信号, 就会再次重发数据. 但是超时不代表服务端没收到, 有可能是因为网络延迟, 发送的数据包或 ACK 还在路上, 实际上是收到了, 而这时如果客户端重发数据, 就会导致重复消息, 这个时候就需要判重
那么如何判重? 这也可以使用 ACK , 当服务端给客户端回复 ACK=6
, 意味着所有小于或等于 6 的数据包都已经收到了, 而这个时候如果再收到这个范围的数据包, 服务端就可以直接丢弃不做处理
3. 解决时序错乱问题
网络是会有延迟的, 而且数据包是通过网络中不同节点发送的, 每个节点发送的速率都是不一样的, 就有可能客户端发送了 4, 5, 6 三个包, 这个时候 5, 6 两个包先到, 而 4 号包还在路上, 这个时候就容易出现乱序的问题
而服务端处理的方式也很巧妙, 它会将 5, 6两个包暂时存放, 直到 4 号包的到来, 再给客户端回复 ACK=7
, 如果 4 号包不来, 服务端也不会做 ACK 回复, 那么客户端等到超过指定时间又会重新发送 4, 5, 6 三个包
因此通过消息顺序编号 客户端重发 服务器顺序ACK,实现了客户端到服务器的数据包的不丢、不重、时序不乱
三次握手
TCP 的 三次握手/四次挥手 实在是个老生常谈的问题了, 小菜甚至不愿多花费篇幅来解释, 但为了保证内容的完整性, 咱们本着负责到底的精神继续做次重温 !
我们通过图可以看到有两个比较关键的key/value
, 一个是 seq=x, 一个是 ACK=x 1
- seq: 上面说到了客户端往服务端发送消息, 都会将每个数据包进行编号且排序, 而这个 seq 便是表示发出去的包的编号是 x, 因为 TCP 是全双工的, 所以通信双方一方面要发送自己编号的包, 一方面要向对方确认收到的包, 因此为了优化传输, 会把两个包合在一起传输, 所以就有了在同一个包里既包含 seq, 也包含 ACK
- ACK: ACK 这个词在上面也已经介绍过了, 表示告诉对方我已经收到编号小于或等于 x 的包了, 而在这里就是表示小于或等于 x 的包都已经收到了, 接下来要准备接收 x 1 的包
为什么要三次握手, 两次不行吗? 四次不行吗?
这是一道经典面试题 ~ 这里直接给出肯定的回答: 两次不行, 四次可以但没必要
通信之前我们需要确认双方都要具备 收发 能力, 才有进行下一步的必要, 不然就是扯淡 ! 举个通俗的例子
"喂, 你爱我吗" "我爱你呀, 你爱我吗" "我也爱你"
恋爱三部曲, 首先要确认双方是否相爱, 如果不确认是否相爱, 那有进行谈恋爱的必要吗
如果变成了两次握手
"喂, 你爱我吗" "我爱你呀, 你爱我吗"
两次握手无法确定双发是否相爱, 那么恋情就无法继续下去, 就容易发展误会, 最后不了了之
那么四次握手呢? 既然三次握手就可以确认双发是否相爱, 四次握手肯定也行, 但却没必要
因此三次握手恰好可以保证客户端和服务器对自己的发送、接收能力做了一次确认。第一次,客户端给服务器发了seq=x,无法得到对方是否收到;第二次,对方回复了seq=y,ACK=x 1。这时客户端知道自己的发送和接收能力没有问题,但服务器只知道自己的接收能力没问题;第三次,客户端发送了ACK=y 1,服务器收到后知道自己第二次发的ACK对方收到了,发送能力也没问题
四次挥手
相比于建立连接的三次握手,关闭连接的四次挥手会显得更加复杂.
四次挥手也得因于 TCP 是全双工的, 有个 Half-Close
状态, 我们可以来模拟对话
客户端: "Hi, 服务器, 我要关闭连接了" 服务器: "哦好的, 我知道了, 等我下" 服务器: "我好了, 关闭连接吧" 客户端: "好的"
这里可以思考下, 为什么不进行三次挥手呢?
客户端: "Hi, 服务器, 我要关闭连接了" 服务器: "哦好的, 我知道了, 关闭连接吧" 客户端: "好的"
感觉三次挥手也没毛病, 四次挥手中的 等我下 是等什么呢? 这里我们需要重点关注下
连接是逻辑上的连接
, 也就是说 连接是假的, 并不想我们现实中的物理连接, 具有实体意义. 那么如果直接进行了三次挥手, 就会导致一些还在路上的数据包 迷路闲逛. 那么问题就来了, 如果连接断了就断了, 这些数据包直接丢弃即可, 但是连接可能重连, 这就会导致之前闲逛的数据包在新连接打开后被当做新的数据包, 就可能出现错乱的问题
解决:
在 TCP/IP 网络中, 有个值叫做 MSL ( Maximum Segment Lifetime ), 任何一个IP数据包在网络上逗留的最长时间是 MSL. 这个值默认是 120 s, 意味着一个数据包必须在 MSL 时间范围内到达目的地, 如果超出了这个时间, 中间的路由节点就会将这个数据包丢弃. 因此可以基于限定, 服务端在知道要关闭连接的时候可以等它个 2*MSL 时间, 然后进入关闭阶段.
结论:
一个连接并不是想关就能立刻关的,关闭后还要等2*MSL
时间才能重开。
考验你有没有做黑客的时候到了 ~ 这会导致什么问题呢?
如果频繁地创建连接,最后可能导致大量的连接处于TIME_WAIT状态,最终耗光所有的连接资源。
因此我们需要采取以下措施
- 不要让服务器主动关闭连接。这样服务器的连接就不会处于TIME_WAIT状态。
- 客户端做连接池,复用连接,而不要频繁地创建和关闭,这其实也是HTTP 1.1和HTTP/2采用的思路。
六. QUIC
HTTP 2.0 已经给万维网带来了新的特性和速度的提高. 但是作为开发者来说, 不满足是不断提升的动力来源 !
QUIC 是一个全新的概念, 它在性能和安全性上又有了一个重大的飞跃, 它可以取代 HTTP/2, 成为 HTTP/3 ! 主要带来了以下几个改进
1. 大大减少了连接建立时间
我们在上面已经了解到了要建立一个 HTTPS 连接需要七次握手 ( TCP 三次握手 SSL/TLS 四次握手 ), 想要较少连接建立的时间, 那么就得从握手上面入手, 也就是减少 RTT 的次数. 而对于 QUIC 协议, 可以把前面的七次握手, 减为 0
次!
图源网, 侵删
2. 无队头阻塞的多路复用
我们重新认识下什么是 队头阻塞, HTTP/2
只要使用 TCP,就没有办法完全解决队头阻塞问题,因为 TCP 是先发送先接收,而 UDP 没有这个限制
那么 QUIC 解决了无队头阻塞问题, 我们就可以很容易的想到因果关系, 那就是 QUIC 是基于 UDP 构建的. 在 QUIC 中丢失一个数据包不会减慢包中其他请求的速度
3. 连接迁移
TCP 的连接是由 4元组 [ 源地址, 源端口, 目标地址, 目标端口 ] 组成的, 在移动端上就会有个问题, 如果客户端是 WIFI 或 4G, 客户端的 IP 是一直在变的, 这会导致什么问题呢? 没错, 这意味着频繁地建立和关闭连接
, 最直观的感受就是, 我们平时看视频的时候如果 WIFI 信号不好, 我们想要切换到 4G 播放的时候, 会等待一会, 然后才能继续播放
那么解决的想法也很简单, 就是在客户端 IP 和 端口 浮动的情况下, 连接仍然可以保持 !
TCP 的连接本来就是假的, 它是一个逻辑上的概念, 既然是逻辑上的概念, QUIC 就可以创造一个逻辑上的连接, 那么就不能以 4元组 的方式来标识连接, 因为它会变, 而是让客户端生成一个 64 位的数字标识连接, 只要这个数字标识不变, 任管 IP 和 端口 如何漂移, 这条连接就会一直存在, 这对于我们的客观感受就是 连接一直存在, 从未中断过
说的花里胡哨的, 那么它究竟有多快? 下面放一张 Google 启用 QUIC 后的趋势图
好了, 伙计们, 到这里我们就已经全面的介绍了网络的概述, 这篇文章并不是深入挖掘的类型, 目的只是让你笼统的认识网络, 毕竟如果没有认识, 就要深入, 那么结局也不会很好, 你说是吧 !
今天的你多努力一点,明天的你就能少说一句求人的话! 我是小菜,一个和你一起变强的男人。