日常记录 - 5.1
重传机制
超时重传:定时器超时后没收到ACK 确认应答,则重发数据
快速重传:三次同样的ACK ,就会触发重传机制。存在问题:出现未收到时,重传一个包还是全部包各有利弊。若是多个包丢失,只重传一个包的情况下,效率低。
SACK(选择性确认) : 需在TCP 头部 【选项】字段加上SACK,可以将已收到的数据的信息发送给「发送方」,可以只重传丢失的数据。
D-SACK:使用了SACK 来告诉 【发送方】有哪些数据被重复接收了。如ACK应答丢失或是网络延时场景,就可以知道哪些数据其实已经成功接收的和出现了网络延迟的问题等。
滑动窗口
发送方窗口
四个部分:已发送并收到ACK 确认的数据、已发送但未收到ACK 确认的数据、未发送但总大小在接收方处理范围内、未发送但总大小超过接收方处理范围

SND.WND:表示发送窗口的大小(大小是由接收方指定的);
SND.UNA(Send Unacknoleged):是一个绝对指针,它指向的是已发送但未收到确认的第一个字节的序列号,也就是 #2 的第一个字节。
SND.NXT:也是一个绝对指针,它指向未发送但可发送范围的第一个字节的序列号,也就是 #3 的第一个字节。
指向 #4 的第一个字节是个相对指针,它需要 SND.UNA 指针加上 SND.WND 大小的偏移量,就可以指向 #4 的第一个字节了。
那么可用窗口大小的计算就可以是:
可用窗口大小 = SND.WND -(SND.NXT - SND.UNA)
接收方窗口
分成三个部分:
#1 + #2 是已成功接收并确认的数据(等待应用进程读取);
#3 是未收到数据但可以接收的数据;
#4 未收到数据并不可以接收的数据;

使用两个指针进行划分:
RCV.WND:表示接收窗口的大小,它会通告给发送方。
RCV.NXT:是一个指针,它指向期望从发送方发送来的下一个数据字节的序列号,也就是 #3 的第一个字节。
指向 #4 的第一个字节是个相对指针,它需要 RCV.NXT 指针加上 RCV.WND 大小的偏移量,就可以指向 #4 的第一个字节了。
接收窗口和发送窗口大小是相等的吗?
约等于。
滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,接收窗口可以很快就空缺出来,但新的接收窗口大小,是通过TCP 报文发给发送方的,存在延迟。
若引入拥塞窗口概念,则发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。
糊涂窗口综合征
窗口越来越小,发送的开销(TCP/IP 头部更大)大,浪费资源。
原因:
接收方可以通告一个小的窗口
而发送方可以发送小数据
解决方法:接收方得满足「不通告小窗口给发送方」+ 发送方开启 Nagle 算法,才能避免糊涂窗口综合症。
让接收方不通告小窗口给发送方 ;
接收方通常的策略如下:
当「窗口大小」小于 min( MSS,缓存空间/2 ) 时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。等窗口大小大于时,才把窗口打开让发送方发送数据。
让发送方避免发送小数据 ; 使用Nagle算法。
Nagle算法:满足下列任意条件,才可以发送数据
条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS;
条件二:收到之前发送数据的 ack 回包;
注意,如果接收方不能满足「不通告小窗口给发送方」,那么即使开了 Nagle 算法,也无法避免糊涂窗口综合症,因为如果对端 ACK 回复很快的话(达到 Nagle 算法的条件二),Nagle 算法就不会拼接太多的数据包,这种情况下依然会有小数据包的传输,网络总体的利用率依然很低。
拥塞控制
为什么要有拥塞控制? 避免 【发送方】 的数据填满整个网络。
什么是拥塞窗口?和发送窗口的关系?
拥塞窗口 cwnd是发送方维护的一个的状态变量,它根据网络的拥塞程度动态变化。
发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。
如何知道当前网络出现了拥塞?发生了超时重传,就认为网络出现了拥塞。
拥塞控制有哪些控制算法?
慢启动
拥塞避免
拥塞发生
快速恢复
慢启动
当发送方每收到一个ACK,拥塞窗口cwnd
的大小就会加1。每次发送的包增加了,收到的ACK也增加了。发包个数是呈指数增长的。
慢启动门限: ssthresh (slow start threshold)
当 cwnd < ssthresh 时,使用慢启动算法。
当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
拥塞避免算法
每当收到一个 ACK 时,cwnd 增加 1/cwnd。 把慢启动的指数增长降到线性增长。
拥塞发生
当发生了「超时重传」,则就会使用拥塞发生算法。
这个时候,ssthresh 和 cwnd 的值会发生变化:
ssthresh 设为 cwnd/2,
cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;(也是设为原来cwnd的一半)
进入快速恢复算法
快速恢复
快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
进入快速恢复算法如下:
拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是快速重传机制收到的3个重复ACK);
重传丢失的数据包;
如果再收到重复的 ACK,那么 cwnd 增加 1;
如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值(即原cwnd/2),原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;
快速恢复算法的变化过程如下图:

发送窗口大小 = cwnd * MSS
快速恢复算法过程中,为什么收到新的数据后,cwnd 设置回了 ssthresh ?
首先,快速恢复是拥塞发生后慢启动的优化,其首要目的仍然是降低 cwnd 来减缓拥塞,所以必然会出现 cwnd 从大到小的改变。
其次,过程2(cwnd逐渐加1)的存在是为了尽快将丢失的数据包发给目标,从而解决拥塞的根本问题(三次相同的 ACK 导致的快速重传),所以这一过程中 cwnd 反而是逐渐增大的。
抓包工具
1. tcpdump 在linux下的使用


TCP Fast Open
在第一次连接时,服务端在第二次握手时产生一个加密的Cookie并随SYN-ACK 包一起发给客户端。客户端缓存,当下次需要建立请求时(或直接是数据,如HTTP GET ),同时携带Cookie,可以提前跳过三次握手的过程。
在linux下如何打开Fast Open功能? net.ipv4.tcp_fastopen ;0:关闭 ;1:作为客户端开启 ;2:作为服务端开启;3:都开启。
TCP 流量控制
发送窗口的分析
发送方在一个窗口发出 n 个包,是不是需要 n 个 ACK 确认报文?
不一定,因为 TCP 有累计确认机制,所以当收到多个数据包时,只需要应答最后一个数据包的 ACK 报文就可以了。
mysql
619.只出现一次的最大数字
我的写法: 输出错误,仿佛max聚合函数失效
这段代码中的 max
聚合函数并没有失效。它会返回每个组中 num
列的最大值。但是,由于你在查询中使用了 group by num
,每个组都只包含一个唯一的 num
值,因此 max(num)
就等于该组中唯一的 num
值。此外,由于你在查询中使用了 having count(*) = 1
,只有那些只包含一个行的组才会被返回。因此,这个查询实际上返回了那些在 MyNumbers
表中只出现一次的 num
值。
正确写法:
group by 可以接多列
610 判断三角形
if语句/case语句的应用:
使用单独的if语句:IF(expr1,expr2,expr3)如果 expr1 是TRUE (expr1 <> 0 and expr1 <> NULL), 则 IF() 的返回值为 expr2;否则返回值则为 expr3。IF() 的返回值为数字值或字符串值,具体情况视其所在语境而定