Call this member function to modify a window’s extended style.
7.4.1 模态对话框的创建与模式循环 其实,“模态”并不是对话框的专利,模态特性是封装在CWnd中的。所以,如果采取与模态对话框相同的创建方法,普通窗体也可以是模态的。 这个方法就是在创建窗体后,调用CWnd::RunModalLoop()模式循环函数。 该函数与前面讲过的CWinThread::Run()非常相似,也是一个消息循环泵,而且CWnd:: RunModalLoop()的消息处理还要稍复杂一些。 TRUE; } //通过资源模板创建对话框及其子控件 if (CreateDlgIndirect(lpDialogTemplate, CWnd 下面讲解CWnd::RunModalLoop()是如何工作的。
拥塞窗口 cwnd 变化的规则: 只要网络中没有出现拥塞,cwnd 就会增大; 但网络中出现了拥塞,cwnd 就减少; 那么怎么知道当前网络是否出现了拥塞呢? 那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。 这个时候,ssthresh 和 cwnd 的值会发生变化: ssthresh 设为 cwnd/2, cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1) 拥塞发生算法的变化如下图 TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下: cwnd = cwnd/2 ,也就是设置为原来的一半; ssthresh = cwnd; 进入快速恢复算法 和 ssthresh 已被更新了: cwnd = cwnd/2 ,也就是设置为原来的一半; ssthresh = cwnd; 然后,进入快速恢复算法如下: 拥塞窗口 cwnd = ssthresh +
何时结束指数增长: (1) 若出现超时指示的丢包事件(即拥塞),cwnd置为1,ssthresh(慢启动阈值)设置为 cwnd / 2,并重新开始慢启动。 每个 RTT 只将 cwnd 的值增加一个 MSS。一种通用的方法是对于 TCP 发送方无论何时到达一个新的确认,就将 cwnd 增加一个MSS (MSS/cwnd)字节 。 4、遇到丢包事件(不论是超时还是冗余ACK),都会ssthresh=cwnd/2,只不过cwnd改变不同:超时时说明情况严重,cwnd置为1,并进入慢启动;冗余ACK说明网络继续在交付报文段,情况没那么严重 = cwnd / 2,cwnd = ssthresh + 3·MSS,快速重传,并进入快速恢复,每遇到一个冗余ACK,cwnd = cwnd + MSS,直到遇到新的ACK,cwnd = ssthresh ,并进入拥塞避免;当出现超时时,ssthresh = cwnd / 2,cwnd = 1·MSS,并进入慢启动重新开始。
当发送方收到一个ACK时,Linux TCP通过状态机的状态来决定其接下来的行为,是应该降低拥塞窗口cwnd大小,或者保持cwnd不变,还是继续增加cwnd。如果处理不当,可能会导致丢包或者超时。 所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。 算法如下: 1) 收到一个ACK,则cwnd = cwnd + 1 / cwnd 2) 每当过了一个往返延迟时间RTT,cwnd大小加一。 TCP认为这种情况比较糟糕,反应也比较强烈: 由于发生丢包,将慢启动阈值ssthresh设置为当前cwnd的一半,即ssthresh = cwnd / 2. cwnd重置为1 进入慢启动过程 最为早期的 在进入快速恢复之前,cwnd和ssthresh已经被更改为原有cwnd的一半。快速恢复算法的逻辑如下: cwnd = cwnd + 3 * MSS,加3 * MSS的原因是因为收到3个重复的ACK。
慢启动 此阶段的操作: cwnd设置为一个很小的初始值,这个初始值在不同版本里面是不一样的,有1、3、10等。 每收到一个ack,cwnd = cwnd+1 cwnd*2 cwnd成指数级别递增。 拥塞避免 此阶段的操作: 收到一个ACK时,cwnd = cwnd + 1/cwnd 当每过一个RTT时,cwnd = cwnd + 1 此阶段cwnd成线性增长。 sshthresh = cwnd /2 cwnd 重置为初始值 进入慢启动过程 2)收到3个duplicate ACK,这种情况认为网络情况还可以,反应不是很强烈。 cwnd = cwnd /2 sshthresh = cwnd 进入快速恢复阶段 跳出该阶段的标志:出现一下,处理之后必然跳出到其他状态。 ,那么cwnd = cwnd +1 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了 跳出该阶段的标志:出现一下,处理之后必然跳出到其他状态。
上图中假定发送窗口和拥塞窗口相等: 连接建立完成后,拥塞窗口cwnd为1,表示可以发送一个MSS的数据 当发送方收到一个ACK报文以后,拥塞窗口cwnd增加1变为2,发送方一次可以发送两个MSS的数据 拥塞避免 拥塞避免算法的规则是:每收到一个ACK报文,拥塞窗口cwnd增加1/cwnd。 超时重传拥塞发生 超时重传拥塞发生以后,ssthresh和cwnd会发生变化: ssthresh(慢启动门阀)设置为cwnd/2 cwnd(拥塞窗口)重置为1 上图中可以看出超时重传拥塞发生时,可发送的数据包将会出现一个断崖式的下跌 这种情况下ssthresh和cwnd会发生以下变化: cwnd(拥塞窗口) = cwnd/2,拥塞窗口减少为一半 ssthresh = cwnd 进入快速恢复算法 快速恢复 上图中可以看出,进入快速恢复前 ,ssthresh和cwnd的值已被更新。
当发送方收到一个ACK时,Linux TCP通过状态机的状态来决定其接下来的行为,是应该降低拥塞窗口cwnd大小,或者保持cwnd不变,还是继续增加cwnd。如果处理不当,可能会导致丢包或者超时。 所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。 算法如下: 1) 收到一个ACK,则cwnd = cwnd + 1 / cwnd 2) 每当过了一个往返延迟时间RTT,cwnd大小加一。 TCP认为这种情况比较糟糕,反应也比较强烈: 由于发生丢包,将慢启动阈值ssthresh设置为当前cwnd的一半,即ssthresh = cwnd / 2. cwnd重置为1 进入慢启动过程 最为早期的 在进入快速恢复之前,cwnd和ssthresh已经被更改为原有cwnd的一半。快速恢复算法的逻辑如下: cwnd = cwnd + 3 * MSS,加3 * MSS的原因是因为收到3个重复的ACK。
当发送方收到一个ACK时,Linux TCP通过状态机的状态来决定其接下来的行为,是应该降低拥塞窗口cwnd大小,或者保持cwnd不变,还是继续增加cwnd。如果处理不当,可能会导致丢包或者超时。 所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。 算法如下: 1) 收到一个ACK,则cwnd = cwnd + 1 / cwnd 2) 每当过了一个往返延迟时间RTT,cwnd大小加一。 TCP认为这种情况比较糟糕,反应也比较强烈: 由于发生丢包,将慢启动阈值ssthresh设置为当前cwnd的一半,即ssthresh = cwnd / 2. cwnd重置为1 进入慢启动过程 最为早期的 在进入快速恢复之前,cwnd和ssthresh已经被更改为原有cwnd的一半。快速恢复算法的逻辑如下: cwnd = cwnd + 3 * MSS,加3 * MSS的原因是因为收到3个重复的ACK。
拥塞的标志 1.重传计时器超时 2.接收到三个重复确认 拥塞控制的机制 慢开始与拥塞避免 慢开始 1.慢开始不是指cwnd的增长速度慢(指数增长),而是指TCP开始发送设置cwnd=1。 注意 发送方窗口的上限值=Min(接受窗口rwnd,拥塞窗口cwnd) rwnd>cwnd 接收方的接收能力限制发送方窗口的最大值 rwnd<cwnd 网络的拥塞限制发送方窗口的最大值 每当收到一个ACK,cwnd++; 呈线性上升 每当过了一个RTT,cwnd = cwnd*2; 呈指数让升 阈值ssthresh(slow start threshold),是一个上限,当cwnd 每当收到一个ACK,cwnd = cwnd + 1/cwnd 每当过了一个RTT,cwnd = cwnd + 1 拥塞发生:当发生丢包进行数据包重传时,表示网络已经拥塞。 = cwnd +1 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
拥塞控制四大特点 TCP慢启动、拥塞避免、快重传、快恢复 含义解释 慢启动 拥塞窗口(cwnd)每次增加以二倍的方式增加。 Eg:如果cwnd当前为2,下次cwnd就会变为4. 拥塞避免 拥塞窗口(cwnd)每次增加以加1的方式增加。 Eg:如果cwnd当前为2,下次cwnd就会变为3 快重传 当接收方收到的失序的报文段后,就立刻发出3次重复确认。 快恢复 当发送方接收到连续的三个重复确认时,执行乘法减小方法,sshd(拥塞窗口阈值)减少为当前sshd(拥塞窗口阈值)的一半,然后cwnd变为当前的sshd(拥塞窗口阈值)的一半,并使用拥塞避免的方法进行报文传递 合并解析 初始启动 cwnd的初始值为1,为了防止数据窗口突然增大造成拥塞,刚开始的时候采用慢启动的方法,当第一次发送接收到确认之后,cwnd加倍变为2,以此类推,知道cwnd的数值大于拥塞窗口阈值sshd 网络超时 无论在慢启动阶段还是在拥塞控制阶段,只要网络出现超时,sthresh置为cwnd的一半,将cwnd置为1,并开始使用慢启动的算法进行拥塞窗口扩大。
慢启动的整个过程如下: 初始化 cwnd = 1 经过1个RTT,即收到一个ACK,cwnd = 2^(1) = 2 经过2个RTT, cwnd = 2^(2) = 4 经过3个 RTT, cwnd = “拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞 cwnd = i 经过 1 RTT, cwnd = i+1 2 RTT, cwnd = i+2 3 RTT, cwnd 设置cwnd为ssthresh加上3倍的报文段大小。 每次收到另一个重复的ACK时,cwnd增加1个报文段大小并发送1个分组(如果新的cwnd允许发送)。 ,置cwnd = ssthresh,转入拥塞避免阶段; 如果发生超时重传,则置ssthresh为当前cwnd的一半,cwnd = 1,重新进入慢启动阶段。 2、为什么发生拥塞时,还增加cwnd? 在检测到丢包时,窗口为CWND。这时候网络中最多有cwnd个包(传输中 < CWND)。每当收到一个重复的ACK,则说明有数据包离开网络,达到接收端了。
发送方的发送窗口的上限值应当取为接收方窗口rwnd和拥塞窗口cwnd这两个变量中较小的一个,即发送窗口的上限值为Min[rwnd, cwnd] 当rwnd < cwnd时,是接收方的接收能力限制发送窗口的最大值 例如:开始发送方先设置cwnd=1,发送第一个报文段M1,接收方接收到M1后,ACK返回给发送端,发送端将cwnd增加到2,接着发送方发送M2,再次接受到ACK后将cwnd增加到4...慢启动算法每经过一个传输轮次 ,拥塞窗口cwnd就加倍。 控制过程: TCP连接初始化,将拥塞窗口cwnd设置为1个报文段,即cwnd=1 执行慢开始算法,cwnd按指数规律增长,直到cwnd == ssthresh时,开始拥塞避免算法,cwnd按线性规律增长 当网络发生阻塞,把ssthresh值更新为拥塞前cwnd的一半(12=24/2),cwnd重新设置为1,再按照(2)执行 让拥塞窗口cwnd缓慢地增大,每经过一个往返时间RTT就把发送方的拥塞窗口cwnd
慢开始 发送最初是慢开始, 令cwnd = 1, 只能发送一个报文段. 收到确认后cwnd加倍. 拥塞避免 为了避免cwnd增长过大, 设置慢开始门限ssthresh, 当cwnd >= ssthresh, 进入拥塞避免, 每个轮次cwnd+1. 如果出现超时, ssthresh = cwnd / 2, cwnd = 1, 重新进行慢开始. 快重传 在接收方, 要求每次接收到报文段都应该对最后一个已收到的有序报文进行确认. 因此执行快恢复, 令ssthresh = cwnd / 2. cwnd = ssthresh. 直接进入拥塞避免.
Slow Start 在慢启动的过程中,随着「cwnd」的增加,可能会出现网络过载,其外在表现就是丢包,一旦出现此类问题,「cwnd」的大小会迅速衰减,以便网络能够缓过来。 ,实际上,这里还有一个慢启动阈值「ssthresh」的概念,如果「cwnd」小于「ssthresh」,那么表示在慢启动阶段;如果「cwnd」大于「ssthresh」,那么表示在拥塞避免阶段,此时「cwnd 如何调整「cwnd」到一个合理值 一般来说「cwnd」的初始值取决于MSS的大小,计算方法如下: min(4 * MSS, max(2 * MSS, 4380)) 以太网标准的MSS大小通常是1460, 所以「cwnd」的初始值是3MSS。 先让我们看一下「cwnd」初始值比较小(等于4MSS)的时候会发生什么: Small Window 再看一下「cwnd」初始值比较大(大于15MSS)的时候又会如何: Big Window 明显可见
如下: 连接刚建好,初始化cwnd = 1(当然,通常不会初始化为1,太小),表明可以传一个MSS大小的数据。 每收到一个ACK,cwnd++,线性增长。 每经过一个RTT,cwnd = cwnd * 2,指数增长(主要增长来源)。 如下: 每收到一个Ack,cwnd = cwnd + 1/cwnd,显然,cwnd > 1时无增长。 每经过一个RTT,cwnd++,线性增长(主要增长来源)。 TCP认为这种情况通常比RTO超时好一些,主流实现TCP Reno的调整力度更柔和(TCP Tahoe的实现和RTO超时一样暴躁): ssthresh = cwnd /2 cwnd = cwnd /2 指定的数据包 如果再收到 duplicated Acks,那么cwnd = cwnd +1 如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。
四、TCP拥塞控制算法 TCP跟踪一个拥塞窗口来(cwnd)提供拥塞控制服务,通过调节cwnd值以控制发送速率。那么TCP如何基于丢包事件来设置cwnd值?通过TCP拥塞控制算法来实现。 刚开始在第一个RTT中,cwnd只有1个MSS,发送一个报文; 收到一个ACK,此时cwnd增加1个MSS,在第2个RTT,cwnd为2个MSS,发送两个报文; 会收到两个ACK,此时cwnd增加2个MSS 2、拥塞避免 上面说到,只有当再次启动慢启动并且cwnd增长到大于等于慢启动阈值ssthresh时,才会进入拥塞避免模式,所以当进入到拥塞避免时,cwnd的值大概是在第一次遇到拥塞时的cwnd的值的一半 此时如果继续进行慢启动就会使cwnd的值翻倍从而可能导致拥塞。所以拥塞避免模式对于每次收到的ACK,并不像慢启动那样直接将cwnd增加一个MSS字节,而是增加MSSMSS/cwnd字节。 也就是说当MSS=1460字节,cwnd为14600字节,那么每次收到一个ACK,cwnd增加14601/10字节,只有收到10个ACK,cwnd才会增加一个MSS。
当发送方收到一个Ack时,Linux TCP通过状态机(state)来决定其接下来的行为,是应该降低拥塞窗口cwnd大小,或者保持cwnd不变,还是继续增加cwnd。 所有正在发送的数据标记为丢失,拥塞窗口cwnd设置为一个段(segment),发送方再次以慢启动算法增大拥塞窗口cwnd。 算法如下: 1) 收到一个ACK,则cwnd = cwnd + 1 / cwnd 2) 每当过了一个往返延迟时间RTT,cwnd大小加一。 TCP认为这种情况比较糟糕,反应也比较强烈: 由于发生丢包,将慢启动阈值ssthresh设置为当前cwnd的一半,即ssthresh = cwnd / 2. cwnd重置为1 进入慢启动过程 最为早期的 在进入快速恢复之前,cwnd和ssthresh已经被更改为原有cwnd的一半。快速恢复算法的逻辑如下: cwnd = cwnd + 3 MSS,加3 MSS的原因是因为收到3个重复的ACK。
("CDocument Constructor \n"); } ~CDocument() { printf("CDocument Destructor \n"); } }; class CWnd :public CCmdTarget { public: CWnd() { printf("CWnd Constructor \n"); } ~CWnd() { printf("CWnd Destructor \n"); } }; class CFrameWnd:public CWnd { public: CFrameWnd() { printf("CFrameWnd Constructor \n"); } ~CFrameWnd() { printf("CFrameWnd Destructor \n"); } }; class CView:public CWnd { public
实际发送窗口大小是两者中的较小值: 发送窗口大小=min(cwnd,rwnd)\text{发送窗口大小} = \min(\text{cwnd}, \text{rwnd}) 慢启动的过程 1. 初始状态 当连接建立后,cwnd 的初始值通常是 1 个 MSS(RFC 3390 修改后,可能初始为 2-4 MSS)。 2. 指数增长阶段 每收到一个 ACK,cwnd 增加 1 个 MSS。 到达慢启动阈值(ssthresh) ssthresh 是一个拥塞窗口的阈值,当 cwnd 达到 ssthresh 时,进入 拥塞避免阶段。 在拥塞避免阶段,cwnd 增加方式改为线性增长。 4. 慢启动示例 假设: MSS = 1KB RTT = 100ms 初始 cwnd = 1 MSS ssthresh = 8 MSS 以下是慢启动过程: RTT 发送数据量(cwnd) 累计发送数据量 1 丢包的处理: 超时重传: cwnd 重置为 1 MSS,重新开始慢启动。 快速重传和快速恢复: 如果检测到轻微拥塞(如重复 ACK),cwnd 降低但不回到初始值。