tcp关闭连接时的四次挥手

来源:互联网 发布:log4net sqlserver 编辑:程序博客网 时间:2024/05/10 15:39

众所周知,tcp要3次握手来建立连接,而断开连接却需要4次挥手,为何需要4次挥手,同时需要那么多的wait状态,比如wait1和wait2状态,那是因为在建立连接的时候,收发双方都是“纯净”的,客户端发送syn的时候,服务器并没有什么数据要发送,而只是需要发送一个synack即可,因此连接开始的握手所传输的都是控制数据。反观连接结束的握手则不然,由于tcp是全双工的,且连接结束必须由一方发起,因此当一方发起连接结束的握手时,另一方可能正有数据要发给发起结束握手的一方,而fin的ack则可以由这个数据捎带过去,或者以别的方式发出ack。接收fin的进程可能根本就没有在receive或者send中,那么此时接收端的ack肯定是协议栈发送的,但是对方关闭了tcp这个消息必须想办法让应用程序知道后再由应用程序决定如何做,因此不能指望fin的ack一定有另一个fin,以socket的实现为例,建立连接的时候,如果客户端connect,那么服务器端一定有一个处于listen状态的socket在等待接收客户端的syn报文,但是对于结束连接,客户端和服务器端完全就对等了,不能指望另一端一定在等待接收fin,因此每一端都要显式发送fin。
     针对是主动发送fin还是被动接收fin后再发送fin,tcp连接关闭时两端的状态机转换并不同,主动发送fin的一端在发出fin后进入wait1状态,然后在收到对端的ack后进入wait2状态,在wait2中如果收到了对端的fin,那么就会进入time-wait状态,之所以有一个wait2和time-wait,是因为对端在收到fin和关闭连接之间有一个时间差并且可能最后的一个ack会丢失从而对端重发fin,在些情况下,可能对端的发送缓冲区中的数据还没有发出,也可能对端会重发fin,总之tcp的可靠性在此时已经不能发挥作用了,因此就只有让时间来冲淡一切了。当然time-wait状态以及waitX状态都可以设置超时时间,超时时间一旦过去,连接将被切断,资源将被回收。
     最后举一例来说明连接的断开阶段的问题:client主动断开后经过了一系列阶段后最终进入了time-wait,而server发送给client的一个序列号为s数据d被中间路由器暂存了,client的time-wait超时,此时client又发起了一个对相同server的连接(地址/端口相同),三次握手过后,被暂存的数据d终于被路由器发送到了client,此时client正要接收序列号为s的数据,那么岂不混乱了?如果恰好这个被暂存的数据是前一次server由于没有收到client最后的ack而重新发送的fin分组,那么client就会认为是server断开了连接...因此time-wait是必要的,并且这也解释了为何在client发起连接的时候,序列号要随机生成。