分析性能损耗
密钥交换算法优化
证书优化
会话复用
Session ID
Session Ticket
Pre-shared Key
前面的 Session ID 和 Session Ticket 方式都需要在 1 RTT 才能恢复会话。
而 TLS1.3 更为牛逼,对于重连 TLS1.3 只需要 0 RTT,原理和 Ticket 类似,只不过在重连时,客户端会把 Ticket 和 HTTP 请求一同发送给服务端,这种方式叫 Pre-shared Key。
HTTP/1.1协议的性能问题
HTTPS/2的优化
兼容HTTPS/1.1
头部压缩
开发HPACKE算法,进行压缩
静态字典,只包含了61种高频出现的
动态字典
Huffman编码(压缩算法)
二进制帧
HTTP/2将HTTP/1.1的文本格式改进成了二进制格式传输数据
二进制帧的结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GDl9ZAWT-1693277647829)(.\image-20230821111027488.png)]
并发传输
GOAWAY
,用来关闭 TCP 连接服务器主动推送资源
PUSH_PROMISE
帧传输 HTTP 头部,并通过帧中的 Promised Stream ID
字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体。所属层级
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qv4UBv0b-1693277647830)(E.\image-20230821153524908.png)]
二者概念介绍
remoteFunc
,如果我们还能像调用本地方法那样去调用它,这样就可以屏蔽掉一些网络细节二者的差别
HTTP/2
在前者的基础上做了很多改进,所以性能可能比很多 RPC 协议还要好,甚至连 gRPC
底层都直接用的 HTTP/2
。使用HTTP不断轮询
长轮询
WebSockt
HTTP协议下,同一时间里,客户端和服务端只能有一方主动发数据,是为半双工
WebSockt,将客户端和服务端建立连接,开启全双工通信
WebSockt握手
数据帧
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lFse92ow-1693277647831)(.\image-20230821183337855.png)]
string
)的数据包[]byte
)的数据包字符串"111"
,那它的长度就是3
。
最开始的7bit
的值是 0~125,那么它就表示了 payload 全部长度,只读最开始的7个bit
就完事了。126(0x7E)
。那它表示payload的长度范围在 126~65535
之间,接下来还需要再读16bit。这16bit会包含payload的真实长度。127(0x7F)
。那它表示payload的长度范围>=65536
,接下来还需要再读64bit。这64bit会包含payload的长度。这能放2的64次方byte的数据,换算一下好多个TB,肯定够用了使用场景
TCP头格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yt24RFZk-1693277647832)(.\image-20230821194159907-16926196207351.png)]
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传个接受端主机,每发送一次数据,就累加一次该数据数据字节数的大小。用于解决网络包乱序的问题
确认应答号:指下一次期望收到的数据序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题
控制位
SYN
包之外该为必须设置为1什么是TCP,TCP的作用,以及工作在哪一层?
无论是TCP/IP
还是OSI
模型都是工作在传输层
由于IP
层是不可靠的,所以有了TCP
,因为TCP
是一个工作在传输层的可靠数据传输的服务,它能保证接收端接收的网络包是无损坏、无间隔、非冗余和按序的
TCP是面向连接的、可靠的、基于字节流的传输层通信协议
什么是TCP连接
用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket、序列号和窗口大小称为连接
建立一个TCP连接是需要客户端与服务端搭乘三个信息的共识:
如何唯一确定一个TCP连接
2^32
,客户端端口数最多为2^16
,最多连接数2^48
TCP和UDP的区别与应用场景
FTP
文件传输DNS
、SNMP
等三次握手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LQgBJEKe-1693277647832)(.\image-20230821205040316.png)]
TCP三次握手的原因
SYN
报文在网络中阻塞,客户端没有接收到 ACK
报文,就会重新发送 SYN
,由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的 ACK
报文,所以服务端每收到一个 SYN
就只能先主动建立一个连接TCP每次建立连接时,初始化的序列号都不一样
初始化序列号ISN的产生
ISN = M + F(localhost, localport, remotehost, remoteport)
M
是一个计时器,这个计时器每隔 4 微秒加 1。F
是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出,用 MD5 算法是一个比较好的选择。四次挥手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rj1Ca7r2-1693277647833)(.\image-20230822194426564.png)]
为什么挥手需要四次
FIN
时,仅仅表示客户端不再发送数据了但是还能接受数据。FIN
报文时,先回一个ACK
应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送FIN
报文给客户端来表示同意现在关闭连接第一次挥手丢失了
FIN
报文后,会等待服务端发送的ACK
,若是报文丢失了,迟迟收不到ACK
,就会触发超时重传,重发次数由 tcp_orphan_retries
参数控制,这个值一般为3tcp_orphan_retries
后,就不再发送 FIN 报文,则会在等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到第二次挥手,那么直接进入到 close
状态第二次挥手丢失了
表现和第一次挥手报文丢失的表现是一样的,客户端没有收到ACK
,还是会触发超时重传
客户端收到ACK
报文后,客户端就会处于FIN_WAIT2
状态:
对于 close 函数关闭的连接,由于无法再发送和接收数据,所以FIN_WAIT2
状态不可以持续太久,而 tcp_fin_timeout
控制了这个状态下连接的持续时长,默认值是 60 秒。
对于shutdown函数关闭的连接,指定了只关闭发送方向,而接收方向并没有关闭,那么意味着主动关闭方还是可以接收数据的。
此时,如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于 FIN_WAIT2
状态(tcp_fin_timeout
无法控制 shutdown 关闭的连接
第三次挥手丢失了
ACK
报文,然后触发超时重传,仍然有tcp_orphan_retries
参数控制第四次挥手丢失了
ACK
报文,但是客户端已经更改状态为TIME_WAIT
状态了为什么需要TIME_WAIT
状态
TIME_WAIT
过多有什么危害
第一是占用系统资源,比如文件描述符、内存资源、CPU资源、线程资源等
第二是占用端口资源,端口资源也是有限的,一般可以开启的端口为32768~61000
,也可以通过net.ipv4.ip_local_port_range
参数指定范围
客户端的端口可以重复使用吗?
多客户端可以bind同一个端口吗?
客户端 TCP 连接 TIME_WAIT 状态过多,会导致端口资源耗尽而无法建立新的连接吗?
如果客户端都是与同一个服务器(目标地址和目标端口一样)建立连接,那么如果客户端 TIME_WAIT 状态的连接过多,当端口资源被耗尽,就无法与这个服务器再建立连接了。
但是,只要客户端连接的服务器不同,端口资源可以重复使用的
如何解决客户端TCP连接TIME_WAIT过多,导致无法与同一个服务器建立连接的问题
net.ipv4.tcp_tw_reuse
这个内核参数TIME_WAIT
状态,如果该连接处于TIME_WAIT
状态并且TIME_WAIT
状态持续的时间超过1秒,那么就会重用这个连接,然后就可以正常使用该端口了如何优化TIME_WAIT
打开net.ipv4.tcp_tw_reuse
和net.ipv4_tcp_timestamps
选项
# 这是开启ipv4.tcp_tw_reuse的一个前提
net.ipv4.tcp_timestamps=1(默认即为 1)
net.ipv4.tcp_tw_reuse = 1
开启内核参数后,复用处于TIME_WAIT
的socket
为新的连接所用
注意:tcp_tw_reuse
功能只能用客户端(连接发起方),因为开启了该功能,在调用connect(),内核会随机找一个time_wait
状态超过1秒的连接给新的连接复用
net.ipv4.tcp_max_tw_buckets
程序中使用SO_LINGER
,应用强制使用RST关闭
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));
如果l_onoff
为非 0, 且l_linger
值为 0,那么调用close
后,会立该发送一个RST
标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT
状态,直接关闭。
但这为跨越TIME_WAIT
状态提供了一个可能,不过是一个非常危险的行为,不值得提倡
服务器出现大量TIME_WAIT
状态的原因有哪些
TIME_WAIT
状态的TCP连接,就是说明服务器主动断开了很多TCP链接Connection: Keep-Alive
,HTTP/1.1之后都是默认开启的Connection:close
,客户端和服务端只要他们任意一方的HTTP Header中有Connection:close
信息,那么就无法使用HTTP长连接机制keepalive_timeout
参数keepalive_requests
这个参数keepalive_requests
参数的默认值100,对于一些 QPS 比较高的场景,比如超过 10000 QPS,甚至达到 30000 , 50000 甚至更高,如果 keepalive_requests 参数值是 100,这时候就 nginx 就会很频繁地关闭连接,那么此时服务端上就会出大量的 TIME_WAIT 状态服务器出现大量CLOSE_WAIT
状态的原因有哪些
CLOSE_WAIT
出现的原因是被动关闭方调用close函数关闭连接,无法从CLOSE_WAIT
状态转变为LAST_ACK
状态,说明被动关闭方没有调用closeFIN
报文的时候,服务端没办法感知这个事件,那服务端就没机会调用close函数了如果已经建立了连接,但是客户端突然出现故障了怎么办?
客户端出现故障指的是客户端的主机发生了宕机,或者断电的场景。发生这种情况的时候,如果服务端一直不会发送数据给客户端,那么服务端是永远无法感知到客户端宕机这个事件的,也就是服务端的 TCP 连接将一直处于 ESTABLISH
状态,占用着系统资源
为了避免这种情况,TCP搞了个保活机制。原理:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
如果已经建立了连接,但是服务端的进程崩溃会发生什么
ACK
确认应答报文,就会重发该数据,也就是我们常说的超时重传SACK
的东西,它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。SACK
,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack
参数打开这个功能(Linux 2.4 后默认打开)D-SACK
。有了窗口,即使在往返时间较长的情况下,它也不会降低网络通信的效率
那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。
窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除
Window
,**这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。**所以,通常窗口的大小是由接收方的窗口大小来决定的窗口关闭
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9KAF8C3V-1693277647835)(.\image-20230825183312454.png)]
为了解决这个问题,TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。
如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
糊涂窗口综合症
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症
主要现象
接收方可以通告一个小窗口
问题解决:
当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0
,也就阻止了发送方再发数据过来。
等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
发送方可以发送小数据
MSS
并且 数据大小 >= MSS
;ack
回包;接收方得满足「不通告小窗口给发送方」+ 发送方开启 Nagle 算法,才能避免糊涂窗口综合症。
场景
拥塞窗口
拥塞窗口(cwnd
)是发送方维护的一个状态变量,它会根据网络的拥塞程度动态变化的
此时,拥塞窗口(cwnd
)、发送窗口(swnd
)以及接收窗口(rwnd
)的关系
swnd = min(cwnd, rwnd)
变化规则:
cwnd
就会增大;cwnd
就减少;怎样察觉网络出现拥堵
四个算法
ssthresh
)
cwnd
< ssthresh
时,使用慢启动算法。cwnd
>= ssthresh
时,就会使用拥塞避免算法1/cwnd
ssthresh
设置为cwnd/2
cwnd
重置为初始值cwnd = cwnd/2
,也就是设置为原来的一半;ssthresh = cwnd
;cwnd = ssthresh + 3
( 3 的意思是确认有 3 个数据包被收到了);Connection: keep-alive
默认携带keepalive_timeout
参数,用来指定 HTTP 长连接的超时时间,该时间一般为60s,若60s内都没有发起新的请求,定时器一到,就会触发回调函数来释放该连接SO_KEEPALIVE
选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制SO_KEEPALIVE
选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制