❤ TCP三次握手和TCP四次挥手(详解)

228 阅读12分钟

❤ TCP三次握手和TCP四次挥手(详解)

1、认识TCP

TCP是一种面向连接的、可靠的、基于字节流的传输层 通信协议,基于运输连接来传送TCP报文段,TCP运输连接的建立和释放,是每一次面向连接的通信中必不可少的过程。

(简单说:TCP就是协议 )

TCP在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接”,其实是客户端和服务端保存的一份关于对方的信息,如ip地址、端口号等。

TCP可以看成是一种字节流,它会处理IP层或以下的层的丢包、重复以及错误问题

在连接的建立过程中,双方需要交换一些连接的参数。这些参数可以放在TCP头部

一个TCP连接由一个4元组构成,分别是两个IP地址和两个端口号。

一个TCP连接通常分为三个阶段:连接、数据传输、退出(关闭)

TCP的三次握手(建立一个链接)和四次挥手(关闭一个连接)实质就是TCP通信的连接和断开

TCP三次握手(TCP的连接)

四次挥手(TCP的释放过程)

TCP运输连接有以下三个阶段

  • ① 建立TCP连接,也就是通过三报文握手来建立TCP连接
  • ② 数据传送,也就是基于已建立的TCP连接进行可靠的数据传输
  • ③ 释放连接,也就是在数据传输结束后,还要通过四报文挥手来释放TCP连接

当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。 TCP的运输连接管理就是使运输连接的建立和释放都能正常的进行(就是打电话正常接起和挂断)

百度百科给出的解释是:

所谓的“三次握手”:为了对每次发送的数据量进行跟踪与协,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。

给一个大致的图片过程:

image.png

2、TCP报文的头部结构

image.png

图中字段含义:

序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

  • ACK:确认序号有效。
  • FIN:释放一个连接。
  • PSH:接收方应该尽快将这个报文交给应用层。
  • RST:重置连接。SYN:发起一个新连接。
  • URG:紧急指针(urgent pointer)有效。需要注意的是:不要将确认序号ack与标志位中的ACK搞混了。确认方ack=发起方seq+1,两端配对。

3、TCP的连接建立(三次握手)

握手需要在客户和服务器之间交换三个TCP 报文段,称之为三报文握手,目的是为了防止已失效的连接请求报文段突然又传送到了,因而产生错误。

TCP的连接建立要解决以下三个问题:

1、使TCP双方能够确知对方的存在

2、使TCP双方能够协商一些参数( 最大窗口值是否使用窗口扩大选项和时间戳选项,以及服务质量等)

3、使TCP双方能够对运输实体资源(例如缓存大小连接表中的项目等)进行分配

三次握手的整个过程:

》 主动发起TCP连接建立称为TCP客户(client) 
》 被动等待TCP连接建立的应用进程称为TCP服务器(server) 

简单描述:

三次握手的本质是确认双方收发数据的通信能力

❤ 举个栗子:

① 你妈喊你:‘回家吃饭!’,然后你听见了

你的角度知道 (证明能力: 你妈嘴巴喊你 【你妈说的能力】 你耳朵听见你妈说话【你听的能力】)

② 于是你说 :‘哦 知道啦’ 你妈的角度知道 (证明能力: 你说 【你说的能力】 你妈听见【你妈听的能力】)

③ 此刻你妈还不知道第一个里面你知道的能力

然后你妈听见了你说的话: 你妈的角度知道 (证明能力: 你妈说的话 【你妈说的能力】 你能听见你妈说话【你听的能力】)

这,就是三次握手

image.png

(1)第一次握手: 客户端要向服务端发起连接请求,首先客户端随机生成一个起始序列号ISN(比如是100),那客户端向服务端发送的报文段包含SYN标志位(也就是SYN=1),序列号seq=100。

客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认。

(2)第二次握手:

服务端收到客户端发过来的报文后,发现SYN=1,知道这是一个连接请求,于是将客户端的起始序列号100存起来,并且随机生成一个服务端的起始序列号(比如是300)。然后给客户端回复一段报文,回复报文包含SYN和ACK标志(也就是SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发过来的序列号+1)。

也就是服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态。

(3)第三次握手:

客户端收到服务端的回复后发现ACK=1并且ack=101,于是知道服务端已经收到了序列号为100的那段报文;同时发现SYN=1,知道了服务端同意了这次连接,于是就将服务端的序列号300给存下来。然后客户端再回复一段报文给服务端,报文包含ACK标志位(ACK=1)、ack=301(服务端序列号+1)、seq=101(第一次握手时发送报文是占据一个序列号的,所以这次seq就从101开始,需要注意的是不携带数据的ACK报文是不占据序列号的,所以后面第一次正式发送数据时seq还是101)。当服务端收到报文后发现ACK=1并且ack=301,就知道客户端收到序列号为300的报文了,就这样客户端和服务端通过TCP建立了连接。

也就是客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

注意:

以上动作发送的包中没有任何数据,等三次握手完成后客户端与服务器才正式开始传送数据。并且理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP连接都将被一直保持下去。

4、关闭连接(四次挥手)

image.png

比如客户端初始化的序列号ISA=100,服务端初始化的序列号ISA=300。TCP连接成功后客户端总共发送了1000个字节的数据,服务端在客户端发FIN报文前总共回复了2000个字节的数据。

  • 第一次挥手:当客户端的数据都传输完成后,客户端向服务端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)。需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号。
  • 第二次挥手:服务端收到客户端发的FIN报文后给客户端回复确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号1101+1)、序列号seq=2300(300+2000)。此时服务端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为服务端可能还有数据没发完。
  • 第三次挥手:服务端将最后数据(比如50个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)。
  • 第四次挥手:客户端收到服务端发的FIN报文后,向服务端发出确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102。注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接。而服务端一旦收到客户端发出的确认报文就会立马释放TCP连接,所以服务端结束TCP连接的时间要比客户端早一些

5、 思考

  1. 为什么TCP连接的时候是3次?2次不可以吗?

在Google Groups的TopLanguage中看到一帖讨论TCP“三次握手” 贴主提出“TCP建立连接为什么是三次握手?”的问题

有一条回复写道:“这个问题的本质是, 信道不可靠, 但是通信双发需要就某个问题达成一致. 而要解决这个问题, 无论你在消息中包含什么信息, 三次通信是理论上的最小值. 所以三次握手不是TCP本身的要求, 而是为了满足"在不可靠信道上可靠地传输信息"这一需求所导致的. 请注意这里的本质需求,信道不可靠, 数据传输要可靠. 三次达到了, 那后面你想接着握手也好, 发数据也好, 跟进行可靠信息传输的需求就没关系了. 因此,如果信道是可靠的, 即无论什么时候发出消息, 对方一定能收到, 或者你不关心是否要保证对方收到你的消息, 那就能像UDP那样直接发送消息就可以了.”。这可视为对“三次握手”目的的另一种解答思路。

因为TCP是面向连接的运输层协议,在应用程序使用TCP协议之前,必须先建立TCP连接。在传输数据完毕后,必须释放已经建立的TCP连接。并且TCP提供的是可靠交付的服务。且TCP提供全双工通信。
所以要进行三次握手来确保在不可靠信道上可靠的传输信息。
至于为什么进行三次握手前面已经给出了一个解释的角度。

因为需要考虑连接时丢包的问题,如果只握手2次,第二次握手时如果服务端发给客户端的确认报文段丢失,此时服务端已经准备好了收发数(可以理解服务端已经连接成功)据,而客户端一直没收到服务端的确认报文,所以客户端就不知道服务端是否已经准备好了(可以理解为客户端未连接成功),这种情况下客户端不会给服务端发数据,也会忽略服务端发过来的数据。如果是三次握手,即便发生丢包也不会有问题,比如如果第三次握手客户端发的确认ack报文丢失,服务端在一段时间内没有收到确认ack报文的话就会重新进行第二次握手,也就是服务端会重发SYN报文段,客户端收到重发的报文段后会再次给服务端发送确认ack报文。

理解角度: 三次通信是理论上在不可靠信道上进行可靠通信的最小值. 大白话:

  1. 为什么TCP连接的时候是3次,关闭的时候却是4次?
  • 因为只有在客户端和服务端都没有数据要发送的时候才能断开TCP。而客户端发出FIN报文时只能保证客户端没有数据发了,服务端还有没有数据发客户端是不知道的。而服务端收到客户端的FIN报文后只能先回复客户端一个确认报文来告诉客户端我服务端已经收到你的FIN报文了,但我服务端还有一些数据没发完,等这些数据发完了服务端才能给客户端发FIN报文(所以不能一次性将确认报文和FIN报文发给客户端,就是这里多出来了一次)。
  1. 为什么客户端发出第四次挥手的确认报文后要等2MSL的时间才能释放TCP连接?
  • 这里同样是要考虑丢包的问题,如果第四次挥手的报文丢失,服务端没收到确认ack报文就会重发第三次挥手的报文,这样报文一去一回最长时间就是2MSL,所以需要等这么长时间来确认服务端确实已经收到了。

....持续更新中