• 欢迎光临~

三次握手 四次挥手

开发技术 开发技术 2022-08-03 次浏览

 

TCP报文段的首部格式介绍

三次握手 四次挥手

源端口:占 16位2个字节发起通信的那个进程

目的端口:占 16位2个字节接收通信的那个进程

序号(seq):占 32位4 个字节,序号范围[0,2^32-1],序号增加到 2^32-1 后,下个序号又回到 0。TCP 是面向字节流的,通过 TCP 传送的字节流中的每个字节都按顺序编号,而报头中的序号字段值则指的是本报文段数据的第一个字节的序号。例如:我们的seq = 201,携带的数据有100,那么最后⼀个字节的序号就为300,那么下⼀个报⽂段就应该从301开始.

确认号(ack):占 32位4 个字节,期望收到对方下个报文段的第一个数据字节的序号。当标志位ACK值为1时,才能产生有效的确认号ack。并且:ack=seq+1;

RST:当RST=1时,表明TCP连接出现严重错误,此时必须释放连接,之后重新连接,⼜叫重置位.

URG:紧急指针标志位,当URG=1时,表明紧急指针字段有效.它告诉系统中有紧急数据,应当尽快传送,这时不会按照原来的排队序列来传送.⽽会将紧急数据插⼊到本报⽂段数据的最前⾯

ACK:当ACK=1时,我们的确认序列号ack才有效,当ACK=0时,确认序号ack⽆效,TCP规定:所有建⽴连接的ACK必须全部置为1

PSH:推送操作,提示接收端应用程序立即从TCP缓冲区把数据读走

SYN:同步序列号标志位,tcp三次握⼿中,第⼀次会将SYN=1,ACK=0,此时表⽰这是⼀个连接请求报⽂段,对⽅会将SYN=1,ACK=1,表⽰同意连接,连接完成之后将SYN=0

FIN:在tcp四次挥⼿时第⼀次将FIN=1,表⽰此报⽂段的发送⽅数据已经发送完毕,这是⼀个释放链接的标志

PS:ACK、SYN和FIN这些大写的单词表示标志位,其值要么是1,要么是0;ack、seq小写的单词表示序号

16位窗⼝的⼤⼩:win的值是作为接收⽅让发送⽅设置其发送窗⼝⼤⼩的依据.

紧急指针:只有当URG=1时的时候,紧急指针才有效,它指出紧急数据的字节数.

三次握手

三次握手,顾名思义就是客户端与服务端进行三次通信。

三次握手 四次挥手

刚开始客户端处于关闭(Closed)状态,服务器处于监听(Listen)状态。

第一次握手 : 客户端给服务器发送SYN报文,初始的序列号为x,并且需要消耗一个序号。此时客户端进入SYN_SENT状态。 当SYN=而ACK=时,表明这是一个连接请求报文。 注意SYN=1时的报文段是不能携带数据的,因此第一次握手和第二次握手客户端和服务端都不能携带数据。 这是因为如果可以携带数据的话,假如有人想要攻击服务器,只需要每次在第一次握手时在SYN报文放入很多数据,重复发送这些大量的SYN报文,服务器就会花大量内存缓冲这些报文,服务器就更加容易被攻击了。

第一次握手服务端可以看出 :客户端的发送能力,和自己的接收能力处于正常状态

第二次握手:服务端收到来自客户端的SYN报文后,对这个SYN报文确认后,会把自己的SYN报文响应给客户端,此时ACK=1表示确认序列号有效,SYN=1也不能携带数据,并且确认号(ack)的值为传来的seq+1,即为x+1,此时初始序号为y。此时服务器进入SYN_RECV状态

第二次握手客户端可以看出:服务端的发送和接收能力都正常,客户端的发送和接收能力也都正常,但重要的一点是服务端不知道客户端的接收能力是否正常。

第三次握手:客户端收到了服务器SYN+ACK的包,此时客户端处于ESTABLISHED状态并且客户端和服务端均表示同意连接,因此会发送一个ACK报文,确认号ack的值仍为序列号+1,即y+1,初始seq值为x,所以第二个报文段seq+1,即x+1。

第三次握手可携带数据,不携带数据则不消耗数据

第三次握手可以看出: 客户端的发送接收,服务端的发送接收能力均正常。

两次握手为什么行不通

首先举一个生活中常见的例子:

此时你和你的对象拨通了电话(没对象就自己new一个): 你 :“喂,能听到我说话吗?” 你对象 :“亲爱的,我可以听到!” 此时你突然不说话了…

试想一下,你(new 的)对象现在肯定怀疑是不是自己的麦克风有问题或者自己的网有问题,导致你听不见…

因此在这里也是如此,仅两次握手,服务端不知道客户端是否能接收到自己给它的通信,所有三次握手是可行的。 另外,三次握手是安全的,并且节约资源

如果客户端发送请求时出现了丢包情况,因为自己没发送,又重新传了一遍,然而等数据传输完成后客户端和服务端都释放了连接,第二次传输的数据在释放连接前给服务器传了过去,但第一次传输的数据假如由于网络原因滞留的时间长了,在释放连接后到达了服务端,这个时候服务端就会误以为客户端又发出了一次新的请求,服务端确认了客户端第一次发出的报文段并同意建立了新的连接,发送报文给客户端,此时服务端会一直等待客户端的答复,而客户端此时正处于释放连接状态,所以导致白白浪费了资源。

半连接队列和全连接队列

半连接队列(syn queue) 客户端发送SYN包,服务端收到后回复SYN+ACK后,服务端进入SYN_RCVD状态,此时双方还没有完全建立连接,这个时候的socket会放到半连接队列。

全连接队列(accept queue) 当服务端收到客户端的ACK后,socket会从半连接队列移出到全连接队列。当调用accpet函数的时候,会从全连接队列的头部返回可用socket给用户进程。全连接队列中存放的是已完成TCP三次握手的过程,等待被处理的连接,在客户端及服务端的状态均为 ESTABLISHED

 

四次挥手

四次挥手,顾名思义就是客户端和服务端四个步骤的释放连接,断开连接需要发送四个包,别名连接终止协议。由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

 

刚开始客户端和服务端都处于ESTABLISHED状态,假如客户端主动发起关闭请求:

第一次挥手:TCP客户端向服务端发送一个FIN报文,用来关闭客户端到服务端的数据传送,其中包含一个序列号seq=u,发送完后,客户端进入FIN_WAIT_1状态,即主动关闭TCP连接,不再发送数据,(但是可以接收服务器发来的报文),等待服务端回复。

第二次挥手:服务端接收到FIN报文后,会发回一个ACK,表明自己已经接收到了此报文,(此时客户端接就知道服务端接收到了自己的断开连接请求),(但是服务端可能还有数据要传输),并且seq=v,ack的值为序列号+1,此时服务端进入CLOSE_WAIT关闭等待状态。和SYN一样,一个FIN将占用一个序号。这个时候TCP处于半关闭状态,当客户端接收到服务端的回复后,进入FIN_WAIT_2终止等待2状态。

第三次挥手:服务器关闭客户端的连接,发送一个FIN报文给客户端,并且指定一个序列号,ack的值为u+1,此时服务器处于LAST_ACK最后确认状态,等待客户端回应。

第四次挥手 :客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1,此时客户端处于 TIME_WAIT(时间等待状态)。TIME-WAIT状态是为了等待足够的时间以确保远程TCP接收到连接中断请求的确认。

注意 :

这个时候由服务端到客户端的 TCP 连接并未释放掉,客户端需要经过时间等待计时器设置的时间 2MSL(一个报文的来回时间) 后才会进入CLOSED状态(这样做的目的是确保服务端收到自己的 ACK 报文。如果服务端在规定时间内没有收到客户端发来的 ACK 报文的话,服务端会重新发送 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK报文给服务端)。服务端收到 ACK 报文之后,就关闭连接了,处于 CLOSED 状态。

这里解释一下需要等待2MSL的原因:

1、防⽌客户端最后⼀次发给服务器的确认在⽹络中丢失以⾄于客户端关闭,⽽服务端并未关闭,导致资源的浪费。   2、等待最⼤的2msl可以让本次连接的所有的⽹络包在链路上消失,以防造成不必要的⼲扰。   如果客户端t直接closed,然后⼜向服务端r发起了⼀个新连接,我们不能保证这个新连接和刚关闭的连接的端⼝号是不同的。假设新连接和已经关闭的⽼端⼝号是⼀样的,如果前⼀次滞留的某些数据仍然在⽹络中,这些延迟数据会在新连接建⽴后到达服务端,所以socket就认为那个延迟的数据是属于新连接的,数据包就会发⽣混淆。所以客户端要在TIME_WAIT状态等待2倍的MSL,这样保证本次连接的所有数据都从⽹络中消失

为什么客户端需要TIME_WAIT状态: 假设最终的ACK丢失,服务端将重发FIN,客户端必须维护TCP状态信息以便可以重发最终的ACK,否则会发送RST(TCP连接出现错误),结果服务端认为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭),客户端必须进入TIME_WAIT 状态,因为客户端可能面临重发最终ACK的情形。

为什么四次挥手,三次挥手不行吗

首先为什么握手是三次,而挥手是四次:

因为握手的时候并没有数据传输,所以服务端的 SYN 和 ACK 报文可以一起发送,但是挥手的时候有数据在传输,所以 ACK 和 FIN 报文不能同时发送,需要分两步,所以会比握手多一步。

其次为什么三次挥手不可行:

因为服务端在接收到FIN, 往往不会立即返回FIN ,必须等到服务端所有的报文都发送完毕了,才能发FIN。因此先发一个ACK表示已经收到客户端的FIN,延迟一段时间才发FIN。这就造成了四次挥手。 如果是三次挥手会造成: 如果将服务端的两次挥手合为一次,等于说服务端将ACK和FIN的发送合并为一次挥手,这个时候长时间的延迟可能会导致客户端误以为FIN没有到达客户端,从而让客户端不断的重发FIN。所有只能第二次握手先发送ACK确认接收到了客户端的数据,等服务器发送完了数据,再发送FIN包进行第三次挥手。

用一个生活中的案例来说明

前提:假如你在外边玩,此时你拨通了妈妈的电话 1.你:“娘亲俺饿了,能给我做一份香喷喷的合罗面吗” 2.你的妈妈:“好,我现在准备准备,买买菜,等会做好了喊你” 。。。。。。家长做饭的过程。。。。。。 3.你的妈妈:“儿砸,饭做好了,该下面条了,什么时候回来呀” 4.你:“好嘞妈,你先下面条吧,我正在上楼” ( 。。。。最后,你吃上了香喷喷的合罗面,并表示:妈妈的味道。。。。)

通过此例子,第二个步骤和第三个步骤肯定不能合并,因为第二个步骤妈妈确定了,你饿了,但还没有做好饭,所有等饭做好了(数据发完了),才会通知你一切都准备好了。

 
程序员灯塔
转载请注明原文链接:三次握手 四次挥手
喜欢 (0)