TCP 三次握手和四次挥手图解(有限状态机)

传输控制协议(TCP,Transmission Control Protocol)是一种面向链接的、可靠的、基于字节流的传输层通讯协议,由 IETF 的 RFC 793 定义,是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议。 面试

互联网络与单个网络有很大的不一样,由于互联网络的不一样部分可能有大相径庭的拓扑结构、带宽、延迟、数据包大小和其余参数。TCP 的设计目标是可以动态地适应互联网络的这些特性,并且具有面对各类故障时的健壮性。 服务器

应用层向 TCP 层发送用于网间传输的、用 8 位字节表示的数据流,而后 TCP 把数据流分区成适当长度的报文段(一般受该计算机链接的网络的数据链路层的最大传输单元(MTU)的限制)。以后 TCP 把结果包传给 IP 层,由它来经过网络将包传送给接收端实体的 TCP 层。TCP 为了保证不发生丢包,就给每一个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。而后接收端实体对已成功收到的包发回一个相应的确认(ACK);若是发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据包就被假设为已丢失将会被进行重传。TCP 用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。 网络

TCP 消息头

 

下面对每一个字段含义进行解释并发

  • 源、目标端口号字段:占 16 比特 (2 字节)。TCP 协议经过使用”端口”来标识源端和目标端的应用进程。端口号可使用0到65535之间的任何数字。在收到服务请求时,操做系统动态地为客户端的应用程序分配端口号。在服务器端,每种服务在”众所周知的端口”(Well-Know Port)为用户提供服务。socket

  • 顺序号字段:占 32 比特。用来标识从 TCP 源端向 TCP 目标端发送的数据字节流,它表示在这个报文段中的第一个数据字节。在TCP传送的流中,每个字节一个顺序号。e.g.若是一个 TCP 报文段的序号为 301,它携带了 100 字节的数据,就表示这 100 个字节的数据的字节序号范围是 [301, 400],该报文段携带的第一个字节序号是 301,最后一个字节序号是 400。因此顺序号号确保了TCP传输的有序性。函数

  • 确认号字段:占 32 比特。只有 ACK 标志为1时,确认号字段才有效。它包含目标端所指望收到源端的下一个数据字节。好比创建链接时,SYN 报文的 ACK 标志位为 0。spa

  • 头部长度字段:占 4 比特。给出头部占 32 比特的数目。没有任何选项字段的 TCP 头部长度为 20 字节;最多能够有 60 字节的 TCP 头部。4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节操作系统

  • 标志位字段(U、A、P、R、S、F):占 6 比特。各比特的含义以下:窗口大小字段:占 16 比特。此字段用来进行流量控制。单位为字节数,这个值是本机指望一次接收的字节数。.net

    • URG:紧急指针标志,为 1 时表示紧急指针有效,为 0 则忽略紧急指针。设计

    • ACK:确认序号标志,为 1 时表示确认号有效,为 0 表示报文中不含确认信息,忽略确认号字段。

    • PSH:push 标志,为 1 表示是带有 push 标志的数据,指示接收方在接收到该报文段之后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。

    • RST:重置链接标志,用于重置因为主机崩溃或其余缘由而出现错误的链接。或者用于拒绝非法的报文段和拒绝链接请求。

    • SYN:同步序号,用于创建链接过程,在链接请求中,SYN = 1 和 ACK = 0 表示该数据段没有使用捎带的确认域,而链接应答捎带一个确认,即 SYN = 1 和 ACK = 1。

    • FIN:finish 标志,用于释放链接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。

  • TCP 校验和字段:占 16 比特。对整个 TCP 报文段,即 TCP 头部和 TCP 数据进行校验和计算,并由目标端进行验证。

  • 紧急指针字段:占 16 比特。它是一个偏移量,和序号字段中的值相加表示紧急数据最后一个字节的序号。
  • 选项和填充:最多见的可选字段是最长报文大小,又称为 MSS(Maximum Segment Size),每一个链接方一般都在通讯的第一个报文段(为创建链接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不必定是32位的整数倍,因此要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。
  • 数据部分 TCP 报文段中的数据部分是可选的。在一个链接创建和一个链接终止时,双方交换的报文段仅有 TCP 首部。若是一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多状况中,也会发送不带任何数据的报文段。

TCP 三次握手

TCP是一个面向链接的协议,不管哪一方向另外一方发送数据以前,都必须先在双方之间创建一条链接,创建一条链接有如下过程。

  • 第一次握手:创建链接时,客户端发送 syn 包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

  • 第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=x+1),同时本身也发送一个 SYN 包(syn=y),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态;

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

这三个报文段完成链接的创建,这个过程成为三次握手。

SYN 攻击

在三次握手过程当中,Server 发送 SYN-ACK 以后,收到 Client 的 ACK 以前的 TCP 链接称为半链接(half-open connect),此时 Server 处于 SYN_RCVD 状态,当收到 ACK 后,Server 转入 ESTABLISHED 状态。

SYN 攻击就是 Client 在短期内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包,Server 回复确认包,并等待 Client 的确认,因为源地址是不存在的,所以,Server 须要不断重发直至超时,这些伪造的 SYN 包将长时间占用未链接队列,致使正常的 SYN 请求由于队列满而被丢弃,从而引发网络堵塞甚至系统瘫痪。

SYN 攻击时一种典型的 DDOS 攻击,检测 SYN 攻击的方式很是简单,即当 Server 上有大量半链接状态且源 IP 地址是随机的,则能够判定遭到 SYN 攻击了。  

解决方案:

  • 一、无效链接监视释放

  • 二、延缓TCB分配方法

当开放了一个 TCP 端口后,该端口就处于 Listening 状态,不停地监视发到该端口的 Syn 报文,

一旦接收到 Client 发来的 Syn 报文,就须要为该请求分配一个 TCB(Transmission Control Block),并返回一个 SYN ACK 命令,当即转为 SYN-RECEIVED 即半开链接状态。

一般一个 TCB 至少须要 280 个字节,在某些操做系统中 TCB 甚至须要 1300 个字节,而某些操做系统在 SOCK 的实现上最多可开启 512 个半开链接。

消耗服务器资源主要是由于当 SYN 数据报文一到达,系统当即分配 TCB,从而占用了资源。

解决:

收到 SYN 数据报文时不急于去分配TCB,而是先回应一个SYN ACK报文,并在一个专用HASH表(Cache)中保存这种半开链接信息,直到收到正确的回应ACK报文再分配TCB。

TCP终止链接过程(四次挥手)

  1. 客户端进程发出链接释放报文,而且中止发送数据。释放数据报文首部,FIN=1,其序列号为 seq=u(等于前面已经传送过来的数据的最后一个字节的序号加 1),此时,客户端进入 FIN-WAIT-1(终止等待1)状态。 TCP 规定,FIN报文段即便不携带数据,也要消耗一个序号。

  2. 服务器收到链接释放报文,发出确认报文,ACK=1,ack=u+1,而且带上本身的序列号 seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,可是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。

  3. 客户端收到服务器的确认请求后,此时,客户端就进入 FIN-WAIT-2(终止等待2)状态,等待服务器发送链接释放报文(在这以前还须要接受服务器发送的最后的数据)。

  4. 服务器将最后的数据发送完毕后,就向客户端发送链接释放报文,FIN=1,ack=u+1,因为在半关闭状态,服务器极可能又发送了一些数据,假定此时的序列号为 seq=w,此时,服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认。

  5. 客户端收到服务器的链接释放报文后,必须发出确认,ACK=1,ack=w+1,而本身的序列号是 seq=u+1,此时,客户端就进入了 TIME-WAIT(时间等待)状态。注意此时 TCP链接尚未释放,必须通过 2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入 CLOSED 状态。

  6. 服务器只要收到了客户端发出的确认,当即进入 CLOSED 状态。一样,撤销 TCB 后,就结束了此次的 TCP 链接。能够看到,服务器结束 TCP 链接的时间要比客户端早一些。

TCP 状态机

 

上图是 TCP 的状态机。

  1. CLOSED:状态时初始状态。

  2. LISTEN:被动打开,服务器端的 状态变为 LISTEN (监听)。被动打开的概念:链接的一端的应用程序通知操做系统,但愿创建一个传入的链接。这时候操做系统为链接的这一端创建一个链接。与之对应的是主动链接:应用程序经过主动打开请求来告诉操做系统创建一个链接。

  3. SYNRECVD:服务器端收到 SYN 后,状态为 SYN;发送 SYN ACK;

  4. SYN_SENTY:应用程序发送 SYN 后,状态为 SYN_SENT;

  5. ESTABLISHED:SYNRECVD 收到 ACK 后,状态为 ESTABLISHED; SYN_SENT 在收到 SYN ACK,发送 ACK,状态为 ESTABLISHED;

  6. CLOSE_WAIT:服务器端在收到 FIN 后,发送 ACK,状态为 CLOSE_WAIT;若是此时服务器端还有数据须要发送,那么就发送,直到数据发送完毕;此时,服务器端发送FIN,状态变为 LAST_ACK;

  7. FIN_WAIT_1:应用程序端发送 FIN,准备断开 TCP 链接;状态从 ESTABLISHED -> FIN_WAIT_1;

  8. FIN_WAIT_2:应用程序端只收到服务器端得 ACK 信号,并无收到FIN信号;说明服务器端还有数据传输,那么此时为半链接;

  9. TIME_WAIT:有两种方式进入该状态:

    • FIN_WAIT_1进入:此时应用程序端口收到 FIN+ACK(而不是像 FIN_WAIT_2 那样只收到 ACK,说明数据已经发送完毕)并向服务器端口发送 ACK;

    • FIN_WAIT_2进入:此时应用程序端口收到了 FIN,而后向服务器端发送 ACK;TIME_WAIT 是为了实现 TCP 全双工链接的可靠性关闭,用来重发可能丢失的 ACK 报文;须要持续 2 个 MSL (最大报文生存时间):假设应用程序端口在进入 TIME_WAIT后,2 个 MSL 时间内并无收到FIN,说明应用程序最后发出的 ACK 已经收到了;不然,会在 2 个 MSL 内在此收到ACK报文;

客户端应用程序的状态迁移图

客户端的状态能够用以下的流程来表示:

CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED

以上流程是在程序正常的状况下应该有的流程,从书中的图中能够看到,在创建链接时,当客户端收到 SYN 报文的 ACK 之后,客户端就打开了数据交互地链接。而结束链接则一般是客户端主动结束的,客户端结束应用程序之后,须要经历 FIN_WAIT_1,FIN_WAIT_2 等状态,这些状态的迁移就是前面提到的结束链接的四次握手。

服务器的状态迁移图

服务器的状态能够用以下的流程来表示:

CLOSED -> LISTEN -> SYN 收到 -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED

在创建链接的时候,服务器端是在第三次握手以后才进入数据交互状态,而关闭链接则是在关闭链接的第二次握手之后(注意不是第四次)。而关闭之后还要等待客户端给出最后的ACK 包才能进入初始的状态。

其余状态迁移

书中的图还有一些其余的状态迁移,这些状态迁移针对服务器和客户端两方面的总结以下

  • LISTEN -> SYN_SENT,对于这个解释就很简单了,服务器有时候也要打开链接的嘛。

  • SYN_SENT -> SYN 收到,服务器和客户端在 SYN_SENT 状态下若是收到 SYN 数据报,则都须要发送 SYN 的 ACK 数据报并把本身的状态调整到 SYN 收到状态,准备进入ESTABLISHED

  • SYN_SENT -> CLOSED,在发送超时的状况下,会返回到 CLOSED 状态。

  • SYN_收到 -> LISTEN,若是受到 RST 包,会返回到 LISTEN 状态。

  • SYN_收到 -> FIN_WAIT_1,这个迁移是说,能够不用到 ESTABLISHED 状态,而能够直接跳转到 FIN_WAIT_1 状态并等待关闭。

2 MSL 等待状态

书中给的图里面,有一个 TIME_WAIT 等待状态,这个状态又叫作 2 MSL 状态,说的是在 TIME_WAIT2 发送了最后一个 ACK 数据报之后,要进入 TIME_WAIT 状态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的(注意这不是四次握手,这是第四次握手的保险状态)。这个状态在很大程度上保证了双方均可以正常结束,可是,问题也来了。

因为插口的 2MSL 状态(插口是IP和端口对的意思,socket),使得应用程序在 2 MSL 时间内是没法再次使用同一个插口的,对于客户程序还好一些,可是对于服务程序,例如httpd,它老是要使用同一个端口来进行服务,而在 2 MSL 时间内,启动 httpd 就会出现错误(插口被使用)。为了不这个错误,服务器给出了一个平静时间的概念,这是说在 2 MSL 时间内,虽然能够从新启动服务器,可是这个服务器仍是要平静的等待 2 MSL 时间的过去才能进行下一次链接。

FIN_WAIT_2 状态

这就是著名的半关闭的状态了,这是在关闭链接时,客户端和服务器两次握手以后的状态。在这个状态下,应用程序还有接受数据的能力,可是已经没法发送数据,可是也有一种多是,客户端一直处于 FIN_WAIT_2 状态,而服务器则一直处于 WAIT_CLOSE 状态,而直到应用层来决定关闭这个状态。

RST,同时打开和同时关闭

RST 是另外一种关闭链接的方式,应用程序应该能够判断 RST 包的真实性,便是否为异常停止。而同时打开和同时关闭则是两种特殊的 TCP 状态,发生的几率很小。

常见面试题

【问题1】为何链接的时候是三次握手,关闭的时候倒是四次握手?

答:由于当 Server 端收到 Client 端的 SYN 链接请求报文后,能够直接发送 SYN+ACK 报文。其中 ACK 报文是用来应答的,SYN 报文是用来同步的。可是关闭链接时,当 Server 端收到 FIN 报文时,极可能并不会当即关闭 SOCKET,因此只能先回复一个 ACK 报文,告诉 Client 端,"你发的 FIN 报文我收到了"。只有等到我 Server 端全部的报文都发送完了,我才能发送 FIN 报文,所以不能一块儿发送。故须要四步握手。

【问题2】为何TIME_WAIT状态须要通过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

虽然按道理,四个报文都发送完毕,咱们能够直接进入 CLOSE 状态了,可是咱们必须假象网络是不可靠的,有能够最后一个 ACK 丢失。因此TIME_WAIT状态就是用来重发可能丢失的 ACK 报文。在 Client 发送出最后的 ACK 回复,但该 ACK 可能丢失。Server 若是没有收到 ACK,将不断重复发送 FIN 片断。因此 Client 不能当即关闭,它必须确认 Server 接收到了该ACK。Client 会在发送出ACK 以后进入到 TIME_WAIT 状态。Client 会设置一个计时器,等待 2MSL 的时间。若是在该时间内再次收到FIN,那么 Client 会重发 ACK 并再次等待2 MSL。所谓的 2 MSL 是两倍的 MSL(Maximum Segment Lifetime)。MSL 指一个片断在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。若是直到 2 MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 链接。

【问题3】为何不能用两次握手进行链接?

3 次握手完成两个重要的功能,既要双方作好发送数据的准备工做(双方都知道彼此已准备好),也要容许双方就初始序列号进行协商,这个序列号在握手过程当中被发送和确认。

如今把三次握手改为仅须要两次握手,死锁是可能发生的。做为例子,考虑计算机 S 和 C 之间的通讯,假定 C 给 S 发送一个链接请求分组,S 收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S 认为链接已经成功地创建了,能够开始发送数据分组。但是,C 在 S 的应答分组在传输中被丢失的状况下,将不知道 S 是否已准备好,不知道 S 创建什么样的序列号,C 甚至怀疑 S 是否收到本身的链接请求分组。在这种状况下,C 认为链接还未创建成功,将忽略 S 发来的任何数据分组,只等待链接确认应答分组。而 S 在发出的分组超时后,重复发送一样的分组。这样就造成了死锁。

【问题4】若是已经创建了链接,可是客户端忽然出现故障了怎么办?

TCP 还设有一个保活计时器,显然,客户端若是出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会从新复位这个计时器,时间一般是设置为 2 小时,若两小时尚未收到客户端的任何数据,服务器就会发送一个探测报文段,之后每隔 75 秒钟发送一次。若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭链接

参考文章

https://blog.csdn.net/whuslei/article/details/6667471

https://blog.csdn.net/qq_38950316/article/details/81087809

https://www.sohu.com/a/321838267_495675

https://www.iteye.com/blog/uule-2213562

相关文章
相关标签/搜索