TCP协议三次握手和四次握手
前言
先说一下IP协议和TCP协议,IP协议是无连接的通信协议,IP不会占用两个设备之间通信的线路,IP实际上主要负责将每个数据包路由至目的地,但是IP协议并没有能够确保数据包是否到达,传过去的数据包是否按照顺序排列,所以IP数据包是不可靠的。而解决数据不可靠的问题就是由TCP协议来完成,接下来就介绍TCP协议,是如何让这些数据可靠的。
TCP概念
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,前面的博客有介绍,在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。数据传输时,应用程序向TCP层发送数据流,TCP就会将接受到的数据流切分成报文段(会根据当前网络环境来调整报文段的大小),然后经过下面的层层传递,最终传递给目标节点的TCP层。为了防止丢包,TCP协议会在数据包上标有序号,对方收到则发送ACK确认,未收到则重传。这个步骤就是我们通常所说的TCP建立连接的三次握手。同时TCP会通过奇偶校验和的方式来校验数据传输过程中是否出现错误。下面来详细介绍三次握手的过程。
TCP报文中的6个标志位
先来简单介绍一下TCP报文
在这里主要介绍一下那6个标志位
URG:紧急指针标志,当为1时表示紧急指针有效,为0则忽略紧急指针
ACK:确认序号标志,为1表示确认号有效,为0表示报文中不含有确认信息,确认号无效
PSH:push标志,当为1时就是让接收方收到该TCP报文的时候不进入缓冲区排队而是快速发给应用程序
RST:重置连接标志,当连接出现错误时可以重置,或者用于拒绝非法的报文段和连接请求
SYN:同步序号,用于建立连接过程
FIN:finish标志,用于释放连接
TCP建立连接的三次握手
下面是示意图:
(注:seq代表序号,ack代表确认号)
第一次握手:当客户端需要去建立连接时,客户端就会发送SYN包(seq=x)到服务器,然后客户端进入SYN_SEND的状态,代表已经发SYN包过去,并且在等待服务器确认。此时ACK=0,SYN=1.,这时候由于才第一次握手,所以没有ACK标志
第二次握手:服务器收到SYN包,就会进行确认,由上面的标志位介绍我们可以知道SYN是表示同步序号,这时候会使得确认号=序号+1,即ack就会等于x+1,然后服务器也会像客户端发送一个SYN包(seq=y),这时候也就是服务器会发送SYN+ACK包,来表示服务器确认到了客户端的一次握手并且二次握手建立,此时服务器进入SYN_RECV状态。此时SYN=1,ACK=1,这时候由于是第二次握手,所以就会有一个服务器给客户端的确认标志。
第三次握手:客户端收到服务器的SYN+ACK包,然后就会像服务器发送确认包ACK(ack=k+1)和SYN(seq=x+1),等到这个包发送完毕之后客户端和服务器就会进入ESTABLISHED状态,完成三次握手,而后就可以在服务端和客户端之间传输数据。此时SYN标志位已经不需要,因为当我们发送ACK标志位的时候代表三次握手成功,已经建立完连接了,接下来可以传送数据过去了。
既然都有SYN包那为什么还要ACK来确认呢?SYN是同步序号,当 SYN=1 而ACK=0 时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使 SYN=1 和 ACK=1。因此SYN置1就表示这是一个连接请求或连接接受报文。而ACK状态是用来确认是否同意连接。也就是传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。
当TCP三次握手完之后,就代表连接已经建立完成,那么连接断开又是怎么样的呢?
TCP四次握手
当在传送完数据之后,客户端会和服务端之间有四次握手
第一次握手:客户端发送一个FIN和序号过去(seq=u),用来表示客户端和服务端之间有关闭的请求,同时关闭客户端到服务端的数据传送,客户端就进入FIN_WAIT_1的状态。
第二次握手:服务端收到FIN=1的标志位时,就会发送一个ACK标志位代表确认,然后确认序号就变成了收到的序号加1,即ack=u+1(FIN和SYN在这点上相同,但是作用不一样)这时候服务端进入CLOSE_WAIT状态,这是一个半关闭状态。只能服务端给客户端发送数据而客户端不能给服务端发送数据。
第三次握手:这次握手还是由服务端发起,这是服务端在传完最后的数据(没有就不传)就会发送一个FIN=1和ACK=1,且序号seq会改变(没有传数据则不变),而ack不变。这时候服务端就会进入LAST_ACK状态,表示最后确认一次。
第四次握手:客户端在接收到FIN之后,就会进入TIME_WAIT状态,接着就发送一个ACK和seq=u+1,ack=w+1给服务端,这时候服务端就会进入CLOSED状态。而客户端进入TIME_WAIT状态的时候必须要等待2MSL的时间才会关闭
为什么会有TIME_WAIT状态呢?(MSL:网络中数据报文存在的最大时间)
1、TIME_WAIT状态可以确保有足够的时间让对方接收到ACK包,如果ACK没有到达,在传输的过程丢失了或者一些其他原因,这样就可以让客户端重发ACK包。如果客户端直接关闭了,那么就有可能导致服务端在一些情况下没有接收到ACK包而无法与客户端断开连接。这样客户端发送ACK包到服务端,服务端请求重发,一来一回就正好是2MSL
2、保证迟来的TCP报文段有足够的时间被识别并丢弃,linux 中一个TCPport不能打开两次或两次以上。当client处于time_wait状态时我们将无法使用此port建立新连接,假设不存在time_wait状态,新连接可能会收到旧连接的数据。
服务器出现大量COLSE_WAIT状态的原因?这时候很可能是因为程序中在收到FIN=1的TCP报文后,由于忙于读或者写,没有去及时发送FIN回来,也就是无法及时关闭连接。这时候需要着重检查释放资源的代码和处理请求的线程配置,有可能是代码中有资源没有被释放掉,也有可能是线程池中的线程数不合理等等