https://blog.csdn.net/lin962792501/article/details/86589026

 TCP协议的主要特点

         1、TCP是面向连接的运输层协议。应用程序使用TCP协议之前,必须先建立TCP连接。传输数据完成之后需要结束连接。

         2、每一条TCP连接只能有两个端点,每一条TCP连接只能是点对点的。

         3、TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复、并且按序到达。

         4、TCP提供全双工通信。TCP允许通信双方的应用程序在任何时候都能发送数据。TCP连接两端都设有发送缓存和接收缓存。用来临时存放双向通信的数据。在发送时,应用程序在把数据传送给TCP的缓存后,就可以做自己的事,而TCP在合适的时候把数据发送出去。在接收时,TCP把收到的数据放入缓存,上层的应用程序在合适的时候读取缓存中的数据。

         5、面向字节流。TCP中的“流”指的是流入到进程或从进程流出的字节序列。“面向字节流”的含义:应用程序和TCP的交互时一次一个数据块(大小不等),但TCP把应用程序交下来的数据看成仅仅是一连串的无结构的字节流。TCP不知道所传送的字节流的含义。TCP不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系。

        TCP连接的建立

        TCP连接的建立是经过三次握手之后完成的。这个过程是面试的时候,面试官喜欢问的点。下面一图展示了三次握手的过程。

如图:初始时候,客户端和服务端都处于CLOSED(关闭)状态。当客户端需要给服务端发送数据包的时候,客户端主动打开连接。这个时候应该是通知了服务端,让服务端也打开连接,所以服务端是被动打开连接。打开连接之后,分别开始创建传输控制模块TCB,接着服务端进入LISTEN(监听)状态,等待客户端的连接请求。客户端也开始准备连接请求数据包,开始发送。客户端发送的第一个数据包是一个连接请求报文段,这个报文的内容如图,是一个同步位SYN=1,另一个是一个初始序号seq=x。TCP规定,SYN=1的报文段不能携带数据,但是消耗一个序列号。客户端发送了这个报文之后,进入SYN-SENT(同步已发送)状态。

      服务端己收到这个数据包之后,知道有客户端请求连接。如果当前有资源,可以同意连接,则给客户端发送确认报文。这个确认报文的内容有:SYN=1(没有变化),seq=y(变成了服务端的序列号),新增ACK=1,ack=x+1(客户端序列号+1)。这里SYN=1,所以报文不能携带数据,同样消耗了服务端的一个序列号。然后服务端进入了SYN-RCVD(同步收到)状态。

     客户端收到服务端的确认报文之后,还需要给客户端发送一个客户端的确认报文。这个报文的内容是ACK=1,seq=x+1,ack=y+1。这里没有了SYN这个字段,所以这个报文可以携带数据。这个客户端确认报文发送出去之后,客户端进入ESTABLISHED(已建立连接)状态。

     服务端接收到这个数据包之后,也进入了ESTABLISHED(已建立连接)状态。

    上面的三个过程就是TCP建立连接的三次握手。

    考虑一个问题:为什么建立连接是三次握手?

        这是因为,首先建立连接,必须要有两次握手吧,客户端主动一次,告知服务端,我想和你建立连接,然后看服务端是否同意。然后如果服务端同意的话,得给一个回复,然后开始等待客户端的数据包,这就是两次握手。如果这个时候就建立连接,客户端开始给服务端发送数据包,在正常情况下没啥问题。但是由于网络并不是100%任何时候都稳定,一旦因为某些原因导致服务端发送给客户端的确认报文丢失,那这个时候客户端收不到确认数据包,会误以为服务端不同意连接,不会给服务端发送数据包,但是这时候服务端已经在等待了。这样的差错会导致服务端一直处于等待状态,浪费资源。

        而三次握手的话,客户端在确认服务端同意之后再发一次数据包给服务端,告知服务端,不管怎么样我都会给你发送数据包的。因为TCP协议中,建立连接之后,每一次发送数据包客户端都会等待服务端的确认信息,如果收不到某一个数据包的确认信息,客户端就会重发这个数据包。这样就不会发生差错了。

 

TCP的连接释放

        前面讲了TCP的连接建立过程是三次握手,而这里讲的TCP连接释放过程却是四次握手,或者两个二次握手,因为一般告别的时候都是挥手,有时候也称为四次挥手。

    当TCP连接需要释放时,客户端和服务端都是处于ESTABLISHED(已建立连接)状态。此时,客户端数据发送完毕,想要结束连接了,主动发出连接释放请求数据包。这个数据包内容:Fin=1,seq=u(这个u是这个数据包之前一个数据包的序列号+1),客户端进入FIN-WAIT-1(终止等待1)状态,不在发送数据包,等待服务端的确认。

        服务端接收到释放数据包之后发出确认,确认包中内容:确认号ack=u+1,序列号seq=v(这个v是服务端上一个发送的数据包的序列号+1),另一个是ACK=1。然后服务端进入CLOSE-WAIT(关闭等待)。这个时候客户端到服务端的连接已经结束了。但是TCP是全双工通信,因为这个时候是客户端主动发起的结束,在服务端这边可能还存在着数据没有完全发送给客户端,所以服务端到客户端仍然没有结束。客户端已经不能在发送数据了,如果服务端还有数据发送过来,客户端仍然要接收。

        客户端收到服务端的确认之后,进入FIN-2(终止等待2)状态,等待服务端发送服务端发器的连接释放数据包。这时候服务端可能还有一些数据包要发送给客户端,客户端一一接收。最后,没有数据要发送了之后,服务端发送连接释放数据包,这个数据包内容:FIN=1,ACK=1,seq=w(因为在服务端回复客户端的连接请求(数据包的序列号是v)之后,可能仍然有其他数据包要发送,所以这里的w不一定是v+1),ack=u+1(确认号和上次回复客户端的请求释放连接的确认号一样)。接着服务端进入LAST-ACK(最后确认状态),等待客户端的确认。

        客户端收到服务端的连接释放数据包之后,发出一个确认数据包,内容:ACK=1,seq=u+1,ack = w+1。然后客户端进入TIME-WAIT(时间等待)状态。这个时候TCP还没有释放。仍需要经过时间等待计时器设置的时间2MSL后,客户端才会进入CLOSED状态。MSL称为最长报文段寿命。RFC793建议把这个值设为2分钟,那这样的话,在客户端收到服务端的连接释放数据包之后,需要等待4分钟才能进入CLOSED状态。这显然时间太长了,不过这个值设为2分钟也只是建议,还是可以设置适合的时间的。最后服务端收到这个客户端的确认包之后就进入了CLOSED状态。显然,服务端一般先于客户端进入关闭状态。

        之所以客户端需要等待2MSL时间才完全结束TCP连接,原因有两个:一、为了保证客户端发送的最后一个确认包能正确到达服务端。因为如果由于网络原因丢失的话,服务端会重新发送连接释放数据包,在等待过程中,如果真的发生这种情况就可以得到处理。客户端每接收到一次服务端发送来的接释放数据包都会重新设置时间等待计时器,然后等待2MSL时间才完全结束TCP连接。二、等待才2MSL时间完全结束TCP连接,可以避免再次开启TCP连接的时候收到上一次TCP连接存在网络中的数据包,显然这样的数据包不是属于本次连接的,是无效的数据包。

        以上就是TCP连接释放的4次握手,也可以叫做四次挥手,也可以看作是两个二次握手(挥手)。 之所以需要四次握手来释放连接,我得看法是,TCP是全双工通信,支持两个方向通信,所以结束的时候,每个方向为了确保数据都能完全从一端到达另一端,所以结束的时候,一端发起结束申请数据包,另一端都要发送确认数据包。两个方向要分开结束,每次结束需要两次握手,所以最终TCP的结束需要4次握手。

      以上就是TCP协议的连接建立过程和释放过程。