TCP/IP 网络模型
应用层:我们电脑或⼿机使⽤的应⽤软件都是在应 ⽤层实现。那么,当两个不同设备的应⽤需要通信的时候,应⽤就把应⽤数据传给下⼀层
传输层:为应用层提供网络支持,两个传输协议,分别是 TCP 和 UDP
网络层:将数据从一个设备传输到另一个设备
数据链路层:让数据在一个链路中传输
物理层:当数据准备要从设备发送到网络时,需要把数据包转换成电信号,让其可以在物理介质中传输
HTTP
HTTP 是超文本传输协议
状态码
1xx
类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
2xx
类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
200 是最常⻅的成功状态码,表示一切正常。如果是非HEAD请求,服务器返回的响应头都会有 body 数据。
204 No Content 也是常⻅的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
206 Partial Content 是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
3xx
类状态码表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。
301 Moved Permanently 表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
302 Found 表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
301 和 302 都会在响应头里使用字段Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。
304 Not Modified 不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,用于缓存控制。
4xx
类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
400 Bad Request 表示客户端请求的报文有错误,但只是个笼统的错误。
403 Forbidden 表示服务器禁止访问资源,并不是客户端的请求出错。
404 Not Found 表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
5xx
类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。
500 Internal Server Error 与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
501 Not Implemented 表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
502 Bad Gateway 通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
503 Service Unavailable 表示服务器当前很忙,暂时无法响应服务器,类似“网络服务正忙,请稍后重试”的意思
http 常见字段
Host字段
客户端发送请求时,用来指定服务器的域名。
Content-Length 字段
服务器在返回数据时,会有Content-Length字段,表明本次回应的数据⻓度。
Connection 字段
Connection字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。
Content-Type 字段
Content-Type字段用于服务器回应时,告诉客户端,本次数据是什么格式。
Content-Encoding 字段
Content-Encoding字段说明数据的压缩方法,表示服务器返回的数据使用了什么压缩格式。
Accept-Encoding字段
客户端在请求时,用Accept-Encoding字段说明自己可以接受哪些压缩方法。
GET 与 POST
GET 和 POST 的区别
Get方法的含义是请求从服务器获取资源,这个资源可以是静态的文本、⻚面、图片视频等。比如打开文章,浏览器就会发送 GET 请求给服务器,服务器就会返回文章的所有文字及资源。
而POST方法则是相反操作,它向URI指定的资源提交数据,数据就放在报文的 body 里。比如,在文章底部,敲入了留言后点击「提交」,浏览器就会执行一次 POST 请求,把你的留言文字放进了报文 body 里,然后拼接好 POST 请求头,通过 TCP 协议发送给服务器。
安全和幂等
在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。
GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。
POST因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。
Cookie
HTTP无状态、明文传输、不安全
无状态的好处,因为服务器不会去记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。
无状态的坏处,既然服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦。
cookie相当于,在客户端第一次请求后,服务器会下发一个装有客户信息的「小贴纸」,后续客户端请求服务器的时候,带上「小贴纸」,服务器就能认得了。
不安全:
通信使用明文(不加密),内容可能会被窃听。比如,账号信息容易泄漏,那你号没了。
不验证通信方的身份,因此有可能遭遇伪装。比如,访问假的淘宝、拼多多,那你钱没了。
无法证明报文的完整性,所以有可能已遭篡改。比如,网⻚上植入垃圾广告,视觉污染。
HTTP 的安全问题,可以用 HTTPS 的方式解决,也就是通过引入 SSL/TLS 层,使得在安全上达到了极致。
HTTP/1.1
⻓连接
管道网络传输
在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。管道机制则是允许浏览器同时发出 A 请求和 B 请求。
队头阻塞
因为当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」。好比上班的路上塞⻋。
HTTP 与 HTTPS
区别
- HTTP 是超文本传输协议,信息是明文传输,存在安全⻛险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
- HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
- HTTP 的端口号是 80,HTTPS 的端口号是 443。
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
HTTPS 解决了 HTTP 的哪些问题
窃听⻛险,比如通信链路上可以获取通信内容,用户号容易没。
篡改⻛险,比如强制植入垃圾广告,视觉污染,用户眼容易瞎。
冒充⻛险,比如冒充淘宝网站,用户钱容易没。
HTTPS在 HTTP 与 TCP 层之间加入了SSL/TLS协议,可以很好的解决了上述的⻛险
信息加密:交互信息无法被窃取,但你的号会因为「自身忘记」账号而没。
校验机制:无法篡改通信内容,篡改了就不能正常显示,但百度「竞价排名」依然可以搜索垃圾广告。
身份证书:证明淘宝是真的淘宝网,但你的钱还是会因为「剁手」而没。
HTTPS 是如何解决上面的三个⻛险的
混合加密的方式实现信息的机密性,解决了窃听的⻛险。
摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的⻛险。
将服务器公钥放入到数字证书中,解决了冒充的⻛险。
HTTPS 是如何建立连接的
客户端向服务器索要并验证服务器的公钥。
双方协商生产「会话秘钥」。
双方采用「会话秘钥」进行加密通信。
SSL/TLS 协议建立的详细流程
ClientHello
客户端主要向服务器发送以下信息:
(1)客户端支持的 SSL/TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数(Client Random),后面用于生产「会话秘钥」。
(3)客户端支持的密码套件列表,如 RSA 加密算法。
SeverHello
服务器回应的内容有如下内容:
(1)确认 SSL/ TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数(Server Random),后面用于生产「会话秘钥」。
(3)确认的密码套件列表,如 RSA 加密算法。
(4)服务器的数字证书。
客户端回应
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
(1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,这样服务器和客户端就同时有三个随机数,接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
服务器的最后回应
服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。然后,向客户端发生最后的信息:
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 SSL/TLS 的握手阶段全部结束。
接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP协议,只不过用「会话秘钥」加密内容。
HTTP/1.1 与 HTTP/1.0
使用 TCP ⻓连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。
HTTP/1.1缺陷
请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩Body的部分;
发送冗⻓的首部。每次互相发送相同的首部造成的浪费较多;
服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞;
没有请求优先级控制;
请求只能从客户端开始,服务器只能被动响应。
HTTP/2改进
头部压缩
在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号。
二进制格式
头信息和数据体都是二进制,并且统称为帧(frame):头信息帧和数据帧
计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率。
数据流
指定数据流的优先级。优先级高的请求,服务器就先响应该请求。
多路复用
一个连接中并发多个请求或回应,而不用按照顺序一一对应
移除了 HTTP/1.1 中的串行请求,不需要排队等待,也就不会再出现「队头阻塞」问题,降低了延迟,大幅度提高了连接的利用率。
服务器推送
服务器不再是被动地响应,也可以主动向客户端发送消息。
HTTP/2 有哪些缺陷?HTTP/3 做了哪些优化
HTTP/2 主要的问题在于,多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协议是不知道有多少个 HTTP 请求的。所以一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP,基于 UDP 的QUIC 协议可以实现类似 TCP 的可靠性传输。
HTTP/1.1如何优化
如何避免发送 HTTP 请求
避免发送 HTTP 请求的方法就是通过缓存技术
客户端会把第一次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,而响应作为 value,两者形成映射关系。
客户端在重新发送请求时,在请求的Etag头部带上第一次请求的响应头部中的摘要,这个摘要是唯一标识响应的资源,当服务器收到请求后,会将本地资源的摘要与请求中的摘要做个比较。
如果不同,那么说明客户端的缓存已经没有价值,服务器在响应中带上最新的资源。
如果相同,说明客户端的缓存还是可以继续使用的,那么服务器仅返回不含有包体的304 Not Modified304 Not Modified响应,告诉客户端仍然有效,这样就可以减少响应资源在网络中传输的延时
如何减少 HTTP 请求次数
减少重定向请求次数
重定向的工作交由代理服务器完成,就能减少 HTTP 请求次数了
当代理服务器知晓了重定向规则后,可以进一步减少消息传递次数
合并请求
减少了重复发送的 HTTP 头部,减少 TCP 连接的数量,因而省去了 TCP 握手和慢启动过程耗费的时间。
通过将多个小图片合并成一个大图片来减少 HTTP 请求的次数,以减少 HTTP 请求的次数,从而减少网络的开销。
延迟发送请求
按需获取,只获取当前用户所看到的⻚面资源,当用户向下滑动⻚面的时候,再向服务器获取接下来的资源。
如何减少 HTTP 响应的数据大小
无损压缩
无损压缩是指资源经过压缩后,信息不被破坏,还能完全恢复到压缩前的原样,适合用在文本文件、程序可执行文件、程序源代码。
有损压缩
有损压缩主要将次要的数据舍弃,牺牲一些质量来减少数据量、提高压缩比,这种方法经常用于压缩多媒体数据,比如音频、视频、图片。
HTTPS RSA 握手解析
TLS 握手过程
通常经过「四个消息」就可以完成TLS 握手,也就是需要 2个 RTT 的时延
TLS 第一次握手
客户端首先会发一个「Client Hello」消息,消息里面有客户端使用的 TLS 版本号、支持的密码套件列表,以及生成的随机数(Client Random),这个随机数会被服务端保留,它是生成对称加密密钥的材料之一。
TLS 第二次握手
当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random),返回「Server Hello」消息,消息里面有服务器确认的 TLS 版本号,也给出了随机数(Server Random),然后从客户端的密码套件列表选择了一个合适的密码套件。
TLS 第三次握手
客户端就会生成一个新的随机数 (pre-master),用服务器的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端,服务端收到后,用 RSA 私钥解密,得到客户端发来的随机数 (pre-master)。双方根据已经得到的三个随机数,生成会话密钥(Master Secret),它是对称密钥,用于对后续的 HTTP请求/响应的数据加解密。生成完会话密钥后,然后客户端发一个「Change Cipher Spec」,告诉服务端开始使用加密方式发送消息。客户端再发一个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信是否可用和之前握手信息是否有被中途篡改过。
TLS 第四次握手
服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双方都验证加密和解密没问题,那么握手正式完成。
RSA 算法的缺陷
使用 RSA 密钥协商算法的最大问题是不支持前向保密。因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
DH 密钥协商算法
即使第三方截获了 TLS 握手阶段传递的公钥,在不知道的私钥的情况下,也是无法计算出密钥的,而且每一次对称加密密钥都是实时生成的,实现前向保密。
TCP
TCP基础
TCP 头格式
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
确认应答号:指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决不丢包的问题。
控制位:
- ACK:该位为1时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的SYN包之外该位必须设置为1。
- RST:该位为1时,表示 TCP 连接中出现异常必须强制断开连接。
- SYN:该位为1时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
- FIN:该位为1时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换FIN位为 1 的 TCP 段。
TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。
什么是 TCP
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
- 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
- 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;
- 字节流:消息是「没有边界」的,所以无论我们消息有多大都可以进行传输。并且消息是「有序的」,当「前一个」消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对「重复」的报文会自动丢弃。
连接:用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接。
- Socket:由 IP 地址和端口号组成
- 序列号:用来解决乱序问题等
- 窗口大小:用来做流量控制
TCP 四元组可以唯一的确定一个连接,四元组包括如下:
源地址、源端口、目的地址、目的端口
UDP 头部格式
目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
包⻓度:该字段保存了 UDP 首部的⻓度跟数据的⻓度之和。
校验和:校验和是为了提供可靠的 UDP 首部和数据而设计。
TCP 和 UDP 区别
连接:
- TCP 是面向连接的传输层协议,传输数据前先要建立连接。
- UDP 是不需要连接,即刻传输数据。
服务对象
- TCP 是一对一的两点服务,即一条连接只有两个端点。
- UDP 支持一对一、一对多、多对多的交互通信
可靠性
- TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。
- UDP 是尽最大努力交付,不保证可靠交付数据。
拥塞控制、流量控制
- TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
- UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
首部开销
- TCP 首部⻓度较⻓,会有一定的开销,首部在没有使用「选项」字段时是20个字节,如果使用了「选项」字段则会变⻓的。
- UDP 首部只有 8 个字节,并且是固定不变的,开销较小。
传输方式
- TCP 是流式传输,没有边界,但保证顺序和可靠。
- UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
分片不同
- TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
- UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层,但是如果中途丢了一个分片,在实现可靠传输的 UDP 时则就需要重传所有的数据包,这样传输效率非常差,所以通常 UDP 的报文应该小于 MTU。
TCP 三次握手
第三次握手是可以携带数据的,前两次握手是不可以携带数据的。
为什么是三次握手
避免历史连接
同步双方初始序列号
当客户端发送携带「初始序列号」的SYN报文的时候,需要服务端回一个ACK应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。
避免资源浪费
如果客户端的SYN阻塞了,重复发送多次SYN报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。
两次握手:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
四次握手:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。
为什么客户端和服务端的初始序列号 ISN 不相同
如果一个已经失效的连接被重用了,但是该旧连接的历史报文还残留在网络中,如果序列号相同,那么就无法分辨出该报文是不是历史报文,如果历史报文被新的连接接收了,则会产生数据错乱。
所以,每次建立连接前重新初始化一个序列号主要是为了通信双方能够根据序号将不属于本连接的报文段丢弃。
另一方面是为了安全性,防止黑客伪造的相同序列号的 TCP 报文被对方接收。
TCP 四次挥手
- 客户端打算关闭连接,此时会发送一个 TCP 首部FIN标志位被置为1的报文,也即FIN报文,之后客户端进入FIN_WAIT_1状态。
- 服务端收到该报文后,就向客户端发送ACK应答报文,接着服务端进入CLOSED_WAIT状态。
- 客户端收到服务端的ACK应答报文后,之后进入FIN_WAIT_2状态。
- 等待服务端处理完数据后,也向客户端发送FIN报文,之后服务端进入LAST_ACK状态。
- 客户端收到服务端的FIN报文后,回一个ACK应答报文,之后进入TIME_WAIT状态。
- 服务器收到了ACK应答报文后,就进入了CLOSED状态,至此服务端已经完成连接的关闭。
- 客户端在经过2MSL一段时间后,自动进入CLOSED状态,至此客户端也完成连接的关闭。
为什么 TIME_WAIT 等待的时间是 2MSL
MSL是 Maximum Segment Lifetime,报文最大生存时间
网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
为什么需要 TIME_WAIT 状态
防止具有相同「四元组」的「旧」数据包被收到;
保证连接正确关闭。
TIME_WAIT 过多有什么危害
第一是内存资源占用;
第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;
TCP 重传、滑动窗口、流量控制、拥塞控制
重传机制
超时重传
超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。
快速重传
滑动窗口
TCP 滑动窗口方案使用三个指针来跟踪在四个传输类别中的每一个类别中的字节。其中两个指针是绝对指针(指特定的序列号),一个是相对指针(需要做偏移)
- SND.WND:表示发送窗口的大小(大小是由接收方指定的);
- SND.UNA:是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。
- SND.NXT:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。
- 指向 #4 的第一个字节是个相对指针,它需要SND.UNA指针加上SND.WND大小的偏移量,就可以指向 #4 的第一个字节了。
可用窗口大小= SND.WND -(SND.NXT - SND.UNA)
接收方的滑动窗口
- #1 + #2 是已成功接收并确认的数据(等待应用进程读取);
- #3 是未收到数据但可以接收的数据;
- #4 未收到数据并不可以接收的数据;
接收窗口和发送窗口的大小是相等的吗?
并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。
流量控制
TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况。
TCP 是如何解决窗口关闭时,潜在的死锁现象呢
TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。
糊涂窗口综合症
如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
解决:
让接收方不通告小窗口给发送方
让发送方避免发送小数据
让接收方不通告小窗口:
当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为0,也就阻止了发送方再发数据过来。
等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
让发送方避免发送小数据:
满足以下两个条件中的一条才可以发送数据
要等到窗口大小 >= MSS或是数据大小 >= MSS
收到之前发送数据的ack回包
拥塞控制
为什么要有拥塞控制
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大
拥塞窗口
拥塞窗口 cwnd是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的
此时发送窗口的值是swnd = min(拥塞窗口, 接收窗口),也就是拥塞窗口和接收窗口中的最小值
只要网络中没有出现拥塞,cwnd就会增大;
但网络中出现了拥塞,cwnd就减少;
怎么知道当前网络是否出现了拥塞
发生了超时重传,就会认为网络出现了用拥塞。
慢启动
当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1
慢启动涨到什么时候
有一个叫慢启动⻔限ssthresh(slow start threshold)状态变量。
当cwnd < ssthresh时,使用慢启动算法。
当cwnd >= ssthresh时,就会使用「拥塞避免算法」。
拥塞避免
进入拥塞避免算法后,每当收到一个 ACK 时,cwnd 增加 1
拥塞发生
当触发了重传机制,也就进入了「拥塞发生算法」
超时重传
快速重传
cwnd = cwnd/2;
ssthresh = cwnd;
进入快速恢复算法
快速恢复
拥塞窗口cwnd = ssthresh + 3( 3 的意思是确认有 3 个数据包被收到了);
重传丢失的数据包;
如果再收到重复的 ACK,那么 cwnd 增加 1;
如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;
拥塞算法示意图