TCP拥塞控制的实现
拥塞控制就是防止过多的数据注入到网络中,这样可以使网络中的路由器或链路器不致过载。拥塞控制多要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、多有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往指点对点通信量的控制,是个端到端的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
TCP进行拥塞控制的算法有四种,即慢开始(slow-start)、拥塞避免(congestionavoidance)、快重传(fast retransmit)和快恢复(fast recovery)
慢开始和拥塞避免
发送方维持一个叫做拥塞窗口(congestion window)的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口
- 慢开始算法的思路是:先探测一下,由小到大逐渐增大发送窗口,也就是说由小到大逐渐增大拥塞窗口数值。使用慢开始算法后,每经过一个传输轮次(transmission round),拥塞窗口cwnd就加倍。
- 拥塞避免算法的思路:是让拥塞窗口cwnd缓慢地增加,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样,拥塞窗口cwnd按现行规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多
慢开始结合拥塞避免算法,ssthresh是算法的切换,窗口大小到达这个值开始从慢开始切换到拥塞避免,到达拥塞上限的时候,将ssthresh减半,窗口设置为1,重新开始。
快重传和快恢复
快重传算法首先要求接受方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等待自己发送数据时才进行确认。
- 当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把慢开始门限ssthresh减半。这是为了预防网络发生拥塞。
- 由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法,而是把cwnd值设置为慢开始门限ssthresh减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。
TCP连接管理
TCP是面向连接的协议。运输连接是用来传送TCP报文的。TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。因此,运输连接就有三个阶段,即:连接建立、数据传送和连接释放。运输连接的管理就是使运输连接的建立和释放都能正常地进行。
TCP连接的建立采用客户服务器方式。主动发起连接建立的应用进程叫做客户(Client)。而被动等待连接建立的应用进程叫做服务器
seq:"sequance"序列号;ack:"acknowledge"确认号;SYN:"synchronize"请求同步标志;;ACK:“acknowledge"确认标志”;FIN:"Finally"结束标志。
三次握手
开始A和B都处于CLOSED状态然后B先进入LISTEN状态,等待请求
- 首先Client端发送连接请求报文,A发送后A进入SYN-SENT状态
- Server段接受连接后回复ACK报文,并为这次连接分配资源,B发送后B进入SYN-RCVD状态
- Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源。A发送后A进入ESTAB-LISHED状态,B收到后也进入该状态,这样TCP连接就建立了。
三次握手的示意图如下:
为什么不是两次握手
为什么要三次握手:防止失效的连接请求报文段突然又传送到主机B。
场景:A首先发送一个连接请求,但是该请求在网络节点上滞留了,没有收到确认。于是A重传了一次请求,并且收到了B的确认,于是连接建立,数据传输完成后,释放连接,假定A发出的第一个请求报文段并没有丢失,而是在某些网络节点上滞留,本来是一个失效的请求,但B收到后误认为是A再次发出一个新请求,于是向A发送确认,同意建立连接。
- 假定采用两次握手,那么只要B发出确认,则新的连接就建立了。由于A并没有发出请求,因此不理会B的确认,也不会向B发送数据,但B却以为新的连接已经建立,并一直等待A的数据,B的许多资源就这样白白浪费了。
- 假定采用三次握手,则B发出确认,但A因为并没有发请求,所以不理会B的确认,B没有收到A的确认,则连接建立失败,B知道连接建立失败。会回收资源。
极端的情况可能由于Client端多次重新发送请求数据而导致Server端最后建立了N多个响应在等待,因而造成极大的资源浪费!所以,“三次握手”很有必要!
为什么不是四次握手
握手握的是序列号,四次握手的过程是
- A发送给B 同步序号SYN+A的seq为x
- B收到后确认收到ACK发给A,然后存储该序号SYN为B的ack=x+1,确认A的seq按序到达
- B向A发送SYN+自己的序列号seq为y
- A收到B的序列号,存储到本地,发送ack=y+1,确认收到了,发送ACK+ack和自己的新序号seq为x+1
由此可以看出,2和3两步可以合成一部,所以没有必要四次握手
四次挥手
TCP连接断开过程:开始的时候A和B都处于ESTAB-LISHED状态
- 假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。A发完后A进入FIN-WAIT-1状态
- 所以你先发送ACK,“告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。B收到后向A发送ack,B发完后B进入COLSE-WAIT状态
- A收到B的确认后进入FIN-WAIT-2状态
- 当B确定数据已发送完成,则向A发送FIN报文,“告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。A收到B的ack就进入Client端收到FIN报文后, "就知道可以关闭连接了。B发完后B进入 LAST-ACK状态
- 但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,“就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!
设置2MSL时间的问题
为什么要设置2MSL的时间:
- 1,为了保证A发送的最后一个ACK能到达B。因为这个ACK可能丢失,因为使B收不到确认,无法关闭,有个这段时间,B就可以超时重传FIN+ACK,然后A就重传一次ACK,然后重置定时器。最后A,B都能顺利关闭,如果没有这段时间,A发送完ACK就关闭,B不一定能顺利关闭。
- 2,防止“已失效连接请求报文段”出现在本连接中,A在发送完最后一个ACK后,再经过2MSL,就可以使本链接持续的时间内所产生的所有报文段都在网络中消失,这样就可以使下一个连接中不再出现这种旧的请求报文段
这个状态标准的持续时间是4分钟
TCP连接状态机
为了更清晰地看出TCP连接的各种状态之间的关系,下图给出了TCP的有限状态机。图中每一个方框即TCP可能具有的状态。每个方框中的大写英文字符串是TCP标准所使用的TCP连接状态名。状态之间的箭头表示可能发生的状态变迁。箭头旁边的字,表明引起这种变迁的原因,或表明发生状态变迁后又出现什么动作。请注意图中有三种不同的箭头。粗实线箭头表示对客户进程的正常变迁。粗虚线箭头表示对服务器进程的正常变迁。另一种细线箭头表示异常变迁