TCP三次握手与四次挥手详解
我们可以清楚的看到TCP报文段其实挺复杂的,有很多字段属性,在此我们先学习其中的六种,这六种就可以帮助我们清楚的学习 大名鼎鼎的 TCP报文三次握手
1.源端口字段和目的端口字段
源端口字段占16比特,两个字节,用来写入源端口号。源端口号用来标识发送给该TCP报文段的应用进程
目的端口字段占16比特,两个字节,用来写入目的端口号。目的端口用来标识接受该TCP报文段的应用进程
2.序号字段(seq)、确认号字段以及确认标志位(ACK)
序号字段占32比特,取值范围是0~2^32 - 1 .当序号增加到最后一个时,下一个序号又回到了0.序号字段的值用来指出本TCP报文段数据载荷的第一个字节的序号
某个TCP报文段由首部和数据载荷两部分构成,数据在和中的每个字节数据都有序号。注意:序号仅仅表示对数据载荷中的字节进行的编号而并非字节本身的内容。对于这个我们的TCP报文首部序号字段的值应该是:166,用来指出本TCP报文段数据载荷的第一个字节的序号
确认号占32比特,取值范围与序号字段相同,当确认号增加到最后一个时,下一个确认号又回到了0.确认号字段的值用来指出期望收到对方下一个TCP报文段的数据载荷的第一个字节的序号,同时也是对之前收到的所有数据的确认。
当确认号为n时表示前n-1个序号为止的所有数据都已经正确接受,接下来期望收到序号为n的数据。
3、确认标志位(ACK)
需要严格记忆的是:只有当ACK取值为1时,确认号字段才有效。ACK取值为0时,确认号字段无效。
TCP规定:在TCP确认连接后所有传送的TCP报文段都必须把ACK置为1;
4、同步标志位(SYN)
同步标志位SYN 是synchronize的缩写,synchronize是使同步的意思
SYN用于TCP双方建立连接
当SYN为1且ACK为0时,表示这是一个TCP连接请求报文段
对方如果同意建立连接,则应该在相应的TCP报文段首部设置SYN=1,ACK=1
也就是说SYN只有两种状态为1或者为0
当syn为1时的两种情况,辨别方法看ack是1还是0即可
要么是一个连接请求报文
要么是一个连接响应报文
当syn为0时表示这并不是以上两种情况的任何一种
5、终止标志位
终止标志位FIN用于释放TCP连接,
当FIN=1时,表面此TCP报文段的发送方已经将全部数据发送完毕,现在要求释放TCP连接
TCP(Transmission control protocol),天生就是为上层应用提供全双工,可靠的数据传输服务的,可靠的其中一个原因是因为TCP是面向连接的,如何进行连接的呢?
TCP的运输连接控制
TCP也就是传输控制协议,内置了运输连接管理特性,运输连接管理就是为了使运输连接的建立和释放都能正常运行
TCP连接运输有以下三个阶段
建立TCP连接:通过三次报文握手来建立
数据传送:基于已经建立的TCP连接进行可靠的数据传输
释放连接:在数据传输结束后,还需要通过四次报文挥手来释放TCP连接
TCP三次报文握手
TCP连接需要解决三个问题
确认网络的可通行,以及使TCP双方能确认对方的存在
使得TCP双方能协商一些参数(最大报文段(MSS)、窗口大小等参数在此阶段确定,为后续数据传输奠定基础)
使得TCP双发能对运输实体资源进行分配和初始化 (比如协商初始序列号(双方通过SYN报文交换初始序列号,使数据按序传输和重组。ISN的随机生成避免与历史连接冲突。),比如(消除旧连接的延迟报文段干扰)网络可能存在之前连接的残留报文。三次握手通过交换新的序列号,确保本次连接不会处理旧报文。)
安全性:防止伪造报文攻击(协商初始序列号)
在实际的通信中, 序号并不是从 1 开始的,而是需要用随机数计算出一个初始值,这是因为 如果序号都从 1 开始,通信过程就会非常容易预测,有人会利用这一点来发动攻击。但是如果初始值是随机的,那么对方就搞不清楚序号到底是从 多少开始计算的,因此需要在开始收发数据之前将初始值告知通信对象。 大家应该还记得在我们刚才讲过的序号字段和SYN同步标志位,有一个将 SYN 控制位设为 1 的连接请求的操作,就是在这一步将序号的初始值告知对方的。实际上,在将 SYN 设为 1 的同时,还需要同时设置序号字段的值,而这里的值就代表序号的初始值。
可靠性:避免旧连接的报文混淆(消除旧连接的延迟报文段干扰)
即使在非恶意环境下,网络也可能出现延迟。假设一个连接关闭了,但它的最后一个报文在网络中“迷路”了。紧接着,同一个双方又建立了新连接。如果新连接的序列号和旧连接一样,那个“迷路”的旧报文可能会突然到达,被错误地当作新连接的数据接收,导致数据混乱。
随机化的序列号使得新旧连接的序列号空间几乎不可能重叠,从而避免了这种“报文串线”的问题。
以下是两台基于TCP即将通信的主机,我们知道请求需要由客户端主动发送给服务器,服务器需要被动接受请求给出相应,在TCP建立连接时也同理,主动发起TCP连接的应用进程称为TCP客户,被动等待TCP连接的应用进程称为TCP服务器。起初两台机器都是处于关闭状态
TCP服务器首先创建传输控制块,然后TCP客户端也会创建传输控制块,这里的传输控制块就是为了存储TCP连接中的一些信息而已,我们之后再提,这个传输控制块为了方便美观我们接下来也不会在画出来。
TCP客户主动向TCP服务器发送TCP连接请求报文段,在连接请求报文段中,SYN被设置为1,表明这是一个连接请求,并没有设置ACK所以并不会是响应请求;接着序号seq初始值被随机的设置为了x,上面我们提到了为什么这里需要seq以及为什么需要随机的设置初始值,这里的seq并不携带任何数据也就是没有数据载荷只是起到标识的作用
TCP规定请求报文字段不能携带任何数据,但是会消耗一个序号。
当TCP服务器接收到客户端发来的TCP请求报文后,如果同意接受请求,则向TCP客户端发送连接请求确认字段
可以看到SYN 和ACK都被设为了1 , 确认号字段ack的值承接上次的客户端的seq序号,代表了前x个序号的字节都已经接收到了,接下来请发送x+1序号起的数据。同理服务器也随机设置了序号seq为y,表示这是服务器选择的起始序号。
TCP规定连接请求确认字段也不能携带任何数据,但是也是要消耗掉一个序号
TCP客户端接收到来自TCP服务器的连接请求报文后,给TCP服务器回送一个普通的TCP确认报文字段,在这个确认报文中,ACK的值被设为1而没有SYN,表示这是一个普通的确认报文字段,并且呢ack=y+1,表示来自服务器的前y个序号的字节都已经接收完毕,seq=x+1承接来自上次服务器的ack=x+1,这次发送的数据就是从x+1起始,保证服务器可以正确接收到连续的数据.
TCP规定普通的TCP确认报字段可以携带数据,但是如果不携带数据,则不消耗序号。换句话说,如果该报文段不携带数据,则TCP客户端进程所发送的下一个数据报文段的序号仍然是x+1
TCP服务器接收到来自客户端的最后一个TCP确认报文字段后,双方都将建立连接,就可以基于面向连接的TCP,进行可靠传输了。
为什么必须是三次握手,而不能是两次握手呢?说的严谨一些就是,为什么TCP客户还需要在最后为TCP服务器发送一个普通确认报文字段
TCP客户端应用进程发送了一个TCP连接请求报文字段,但是该报文字段似乎因为网络问题滞留在网络中的某个节点或者其他情况而没有按时到达应到的TCP 服务器端,这必然会导致超时重传机制(大白话就是超过一定时间重新发了一份)此时重传的请求报文字段被正确的接收
现在由于我们是两次报文握手即可建立连接,TCP服务器对于接收到的请求报文给予连接请求确认报文,现在双方进入连接状态,双方进行一系列激烈的数据传输,终于在某个时间点传递完了全部数据,最后通过四次报文挥手而断开连接(四次报文挥手我们稍后会讲解此处可以认为是双方"一下"就断开了连接)
此时双方都进入关闭状态,而这时,我们姗姗来迟,在网络中耽误的第一次请求报文终于到达了TCP服务器,(如果到达的时间比较巧妙在刚断开的时候,服务器会觉得很奇怪怎么刚断开就又连接了?如果是很晚才到达)TCP服务器仍然会为TCP客户端给予连接请求确认报文,此时服务器进入建立连接的状态
而TCP客户端进程由于已经是关闭状态,虽然可以接收到服务器发来的连接请求确认报文,但是由于客户端进程自己并没有发起新的连接请求,因此并不会理会该报文段
最终,只有TCP服务器一人成功建立连接而并没有客户端的参与,因此服务器会一直等待客户端传递数据,这将白白浪费服务器的很多资源.......
TCP四次报文挥手
当应用程序的数据交换完成后,就会进入运输连接控制的释放连接过程。
如果释放连接失败,客户端和服务器都会积累不必要的信息,从而挤压主机中的信息,因此释放连接要比请求连接更加谨慎小心
TCP连接管理控制始于三次握手,终于四次报文挥手,四次挥手是结束连接控制的处理操作,通讯双方需要互相发送 FIN=1标志,FIN=1标志表示 finished data已经结束的数据,是根据上层应用程序进行分配的,意思就是:比如上层应用程序调用socket.close()或者shutdown()
等Socket API时操作系统TCP协议栈才会生成FIN标志的数据包,而不是TCP协议定时或自动发送FIN。
在请求连接中,我们知道必须由客户端主动发出连接请求给服务器,而在连接释放中,无论哪一端均可主动提出释放连接,先发送FIN=1断开连接的一端为主动关闭,接受这一处理的一端则称为被动关闭
在这里我们不妨默认客户端为主动断开连接的一方
当客户端传输完预定交换的应用数据,接受了来自应用程序的关闭处理的请求时,就会开始主动关闭的处理。此时需要向服务器发送FIN=1标志和ACK=1的FIN/ACK数据包;等待接收来自服务器的FIN/ACK 数据包
服务器收到FIN/ACK数据包后开始被动关闭处理,发送一个与FIN/ACK数据包对应的ACK数据包,并委托应用程序进行关闭处理;等待来自应用程序的关闭处理请求
收到ACK的客户端,继续等待来自服务器的FIN/ACK数据包
当服务器接收到来自应用程序的关闭处理请求时,会向客户端发送自己的ACK/FIN数据包,并等待与与自己对应的ACK/FIN数据包的ACK数据包,这个就是关闭处理中的最后一个ACK
客户端收到来自服务器的FIN/ACK数据包后,会针对该项请求发送ACK数据包
并进入一个特殊的TIME-WAIT状态。TIME-WAIT状态是一种等待可能迟到的ACK数据包的类似于保险的状态
收到ACK数据包的服务器会进行删除连接的操作,并会释放为了连接而预留的资源,这样就完成了被动关闭的处理
转换到TIME-WAIT状态的客户都安会设置等待时间,一直到结束后,也会进行删除连接的操作并会释放为了连接而预留的资源,完成最后的主动关闭的处理