众所周知,在整个网络连接协议中,一个极其重要的部分就是运输层的TCP协议。它负责将数据可靠地进行端到端的传送。在学习TCP的过程当中,又属其连接建立和释放的过程最为重要。因此,在这里我将重读课本后的笔记与思考记录在此。
目的
TCP建立连接和释放连接的目的是什么呢?乍看上去这是一句废话。建立连接的目的不就是建立一条连接以供高层应用使用吗?这当然是进行连接建立的初衷。但是,从结果上来看,TCP连接的建立实际上还达到了以下目的:
- 使两端知道互相的存在
- 进行选项的协商。例如窗口大小
- 为TCP实体分配资源。如缓冲区等
知道了这些,就更能理解TCP协议中各个部分的作用了。
建立连接:三次握手
TCP连接的建立过程就是著名的三次握手。在连接建立的过程中存在着主动方(UNIX下调用connect)和被动方(UNIX下调用listen)。具体连接过程如下:
首先来明确一下各个报文段中出现的内容:
标志位SYN:其为1代表着连接的建立。通常SYN、ACK均为1时代表发送建立连接的请求。
标志位ACK:其为1时代表该报文段包含确认信息。一般传输数据的报文段ACK为0。
顺序号SEQ:代表着本报文段携带用户数据的第一个字节的顺序号。
确认号ACK:代表对传输进来数据的确认,同时也是希望对方下一次发送过来数据的顺序号。
那么整个三次握手的过程并不难理解。双方各自确定好初始的顺序号,经过主动方第三次的确认后,连接建立。第三次的报文段可以携带用户数据,也可以不携带。
建立连接:三次握手?WHY?
如果是从第一部分的目的出发,似乎两次握手就足够达到了。那么为何还需要三次握手呢?就是因为TCP协议的连接层是基于不靠谱的网络层建立的。在TCP的连接中必须要考虑由网络层的不可靠性所带来的重传、丢失、失序等等情况。比如下面的例子:
首先由主动方发出了一条建立连接的请求。但是这个报文段由于延时,在一段时间之后被动方才收到,并立即发出确认报文,此时连接建立。然而在主动方收到确认的报文之前,它又重发了一个建立连接的请求,但这次初始顺序号就产生了变化。被动方接收到这个请求后由于顺序号不同,会认为这是一个错误的报文,将其丢弃;而主动方在发送了第二个请求后接收到了确认报文段,但是由于确认号不同因此也会将其丢弃。这样客户端就不停重发建立请求而服务端就不停地丢弃。
如果使用三次握手,这种情况就可以避免发生。在服务端发送确认报文后,它接收到了来自客户端的第二次请求。由于确认报文迟迟没有确认(第三次握手),服务端就会放弃之前的连接过程,而重新发送对SEQ = Z的确认报文。这样就形成了一个新的三次握手过程,连接成功建立。
除了这种延时的情况之外,还会有两端同时发送建立请求、重发等等特殊情况,三次握手的过程都可以保证建立可靠连接。在此不做过多陈述。
释放连接:四次挥手
释放连接的过程相对简单,如下图:
首先由客户端发送FIN标志位为1的报文,告诉服务端“我这边已经没有要发送的数据了,准备关闭连接”。而服务端就会对此发送一个确认报文。等到服务端也把数据发送完了,它就会发送一个同样的FIN标志位为1的报文。在客户端回应确认报文后,TCP连接正式关闭。
当然单单依靠ACK确认标志是不能完全保证稳妥释放连接的。如果网络不好导致ACK报文一直无法传达到位,在没有其他机制的情况下这个TCP连接就会一直占用着资源。因此TCP的连接释放还引入了超时计时器,在发出FIN报文后就开始计时。如果一定时间间隔内不能收到确认报文,就会重发FIN报文或者是强制释放连接。
除此之外,对于释放连接的主动方,在最终释放连接之前还需要等待两倍的最大报文段生命期(MSL)
。这主要是为了确保这次要释放的TCP连接的报文段(特别是FIN及对其的回应)完全消失在网络之中。
四次挥手的必要性
那么,为何释放连接需要四次挥手呢?为什么不是跟建立连接一样的三次?为什么不是五次六次甚至更多?
首先回答第一个问题。四次挥手的本质其实就是三次握手。看下图:
如果将回应客户端的确认报文和FIN报文合并到一起,那么这实际上也就是一个三次握手的过程;而之所以要将确认报文和FIN报文分离形成四次挥手,是因为服务端需要将所有的数据发送完毕。在这两个报文发送的间隔,服务端还需要发送的数据全部送出,该连接就已经没有存在的必要了。
至于五次、六次挥手,答案就只有三个字:没必要。四次挥手能完成的事情为何还要耗费资源去进行五次、六次呢?