基于C++从0到1手写Linux高性能网络编程框架(超清)
TIME_WAIT状态存在原因有两点:
其一是可靠的中止tcp连接;
其二是保证让延迟的tcp报文有足够的时间被识别;
客户端在关闭连接阶段需要处理收到重复的结束报文,然后回复最后的ACK给服务端,否则客户端在收到服务端的FIN就直接回复ACK,这样后续服务端重传的FIN包都会被回复RESET报文,这时服务端认为是错误报文,这就是第一点存在的原因;
那么第二点是为了不让同一个tcp端口被多次打开或者是断开以后马上被一个新的连接接管,这样存在数据安全和处理异常等问题,让tcp最大时间坚持2MSL也是为了确保重发和延时的tcp包在这段时间内被丢弃(使用端口复用采用socket选项SO_REUSEADDR);
tcp超时重传和拥塞控制
tcp服务必须能够重传超时时间内未收到的tcp报文段。
为此,tcp模块为每一个tcp报文都维护一个重传定时器,linux两个重传相关的内核参数:
/proc/sys/net/ipv4/tcp_retries1和/proc/sys/net/ipv4/tcp_retries2
前者表示tcp最少执行重传次数,默认为3;
后者表示tcp最多执行重传次数,默认为15;
tcp服务有重传必然就会导致拥塞,那么接下来介绍网络底层如何进行拥塞控制?
拥塞控制包括四个部分:慢启动,拥塞避免,快速重传和快速恢复;
在此之前还需要介绍窗口概念:RNWD(接收窗口,指前面tcp报文中的对端发送的win窗口),CWND(拥塞窗口,是系统定义的一个状态变量大小),SWND(发送窗口,是RNWD和CWND之间的较小值);
在tcp模块刚开始发送数据阶段并不知道网络的实际情况,需要试探性地增加CWND,这一过程称为慢启动,CWND初始值设置为2-4个MSS;然后发送端每次收到接受端的一个确认,就按照公式:
CWND += min(N, MSS)
其中N是此次确认中包含的之前未确认的字节数;
如果随着CWND不断累加,不加控制会造成网络拥塞,那么需要进行拥塞避免算法,界定慢启动和拥塞避免过程通过慢启动门限(ssthresh)控制,当CWND超过ssthresh则进入拥塞避免阶段;
拥塞避免阶段控制CWND是每个RTT时间都计算(如果RTT时间内收到多少确认包),公式:
CWND += SMSS*SMSS/CWND
这样就保障了CWND缓慢增长,直到传输超时或者tcp重传定时器溢出,就需要重新调整ssthresh,再次进入慢启动阶段,那么ssthresh计算公式:
ssthresh = max(FlightSize/2, 2MSS)
其中FlightSize已经发送但是还未收到确认的字节数;
另外一种情况:在接受端接收到重复的确认报文段的时候,tcp模块如何处理?
如果发送端收到3个重复的确认报文,认为拥塞发生,启动快速重传和快速恢复,先计算ssthresh;
然后通过CWND = ssthresh + 3 * SMSS计算出CWND,再次每收到1个重复确认时,设置CWND += SMSS,最后当收到新数据的确认时,直接设置CWND = ssthresh,这样快速重传和快速恢复完成,又再次进入拥塞避免阶段。