12 不要用TIME-WAIT暗杀来关闭一条连接
12.1 连接拆除
- 主机1的应用程序关闭自己这端的连接,使得tcp向主机2发送了一个FIN
- 主机2对这个FIN进行ACK,并将这个FIN作为一个EOF传给应用程序
- 一段时间后,主机2上的应用程序关闭了它那端的连接,这样就会向主机1发送1个FIN
- 主机1会以ack应答
此时,主机2关闭连接并释放资源。但是主机1还没有关闭连接,而是会进入TIME-WAIT状态,并将这个时间停留在2MSL(2个最大分段寿命的时间,TTL,MSL被RFC 793定义为2分钟,BSD派生系统为30S,即TIME-WAIT状态会停留1分钟) - 等待了2MSL之后,主机1也将连接关闭,并释放其资源
注意点:
- 只有主动关闭的一端才会进入TIME-WAIT状态(如果同时关闭,两端都进入TIME-WAIT状态)
- 如果连接处于TIME-WAIT状态时有分组到达,就重启2MSL的定时器
12.2 TIME-WAIT的目的
- 维持连接状态,以防主动关闭连接的那端发送的最后一条ACK丢失后造成另一端重新发送FIN信号
注:如果主动关闭的一端没有进入TIME-WAIT状态,而只是关闭了连接,收到重传的FIN时,tcp就已经没有这条连接记录了,所有就用RST来响应,对等实体就会产生一条错误,而不会有序地终止。 - 为耗尽网络中的所有此连接的“走失段”提供时间
注:如果数据报只是延迟,没有丢失或者是对数据报的ack丢失了,那么重传的数据可能会在收到原始数据之后到达。
- 处于TIME-WAIT状态-----tcp会注意到延迟数据的序列号在当前接收窗口之外,并将其丢弃
- 处于连接关闭-----通常tcp只是将数据丢失并以RST进行响应,不会产生什么问题。但是,如果在这两台主机间用同样的端口号建立了一条连接,这条走失的段看起来就像是属于那条新连接,如果走失分段中的任意一个序列号碰巧落在新连接的当前接收窗口中,这部分数据就会被接受,从而对新连接造成破坏。
12.3 TIME-WAIT暗杀(提前终止)
- 当一条连接处于TIME-WAIT状态并收到RST时,连接立即关闭
注:有一条处于TIME-WAIT状态的连接,当有一个原有的重复分段到达,而这个分段是tcp无法接受的(序列号在当前接收窗口之外),tcp会以一个ack回应,说明它所期待的序列号(就是对等实体的FIN之后的序列号),由于对等实体中已经没有这个连接记录了,就以一个RST进行ack(TIME-WAIT暗杀) - 使用套接字选项SO_LINGER迫使连接立即关闭
struct linger
{
int l_onoff;
int l_linger;
}
- 如果 l_onoff为0,其行为与默认行为相同----close或closesocket调用立即返回,内核继续尝试发送所有未发送的数据
- 如果 l_onoff非0,其行为取决于l_linger,如果l_linger非0,close或closesocket在时间间隔到期之前是不会返回的,如果此时仍有未发送的数据,close或closesocket就返回EWOULDBLOCK,所有未发送的数据都可能丢失,这两个调用就都返回0。(使用SO_LINGER,只能保证将数据传递给对等实体的tcp,不能保证数据被应用程序读走了),如果l_linger为0,连接立即丢失,即向对等实体发送一个RST,不经过TIME-WAIT状态立即关闭连接(TIME-WAIT暗杀)