欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

TCP协议三次握手和四次握手

程序员文章站 2024-02-04 22:04:22
前言 先说一下IP协议和TCP协议,IP协议是无连接的通信协议,IP不会占用两个设备之间通信的线路,IP实际上主要负责将每个数据包路由至目的地,但是IP协议并没有能够确保数据包是否到达,传过去的数据包是否按照顺序排列,所以IP数据包是不可靠的。而解决数据不可靠的问题就是由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报文

TCP协议三次握手和四次握手

TCP协议三次握手和四次握手

在这里主要介绍一下那6个标志位

urg:紧急指针标志,当为1时表示紧急指针有效,为0则忽略紧急指针

ack:确认序号标志,为1表示确认号有效,为0表示报文中不含有确认信息,确认号无效

psh:push标志,当为1时就是让接收方收到该tcp报文的时候不进入缓冲区排队而是快速发给应用程序

rst:重置连接标志,当连接出现错误时可以重置,或者用于拒绝非法的报文段和连接请求

syn:同步序号,用于建立连接过程

fin:finish标志,用于释放连接

tcp建立连接的三次握手

下面是示意图:

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四次握手

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回来,也就是无法及时关闭连接。这时候需要着重检查释放资源的代码和处理请求的线程配置,有可能是代码中有资源没有被释放掉,也有可能是线程池中的线程数不合理等等