tcp状态机-三次握手-四次挥手以及常见面试题

TCP状态机介绍

在网络协议栈中,目前只有TCP提供了一种面向连接的可靠性数据传输。而可靠性,无非就是保证,我发给你的,你一定要收到。确保中间的通信过程中,不会丢失数据和乱序。在TCP保证可靠性数据传输的实现来看,超时重传、序列号及数据的应答 这三个特征 就是实现可靠性的最基本保证,而对于tcp窗口大小等等设置,也是保证可靠性的一个方面。所有的目的只为一个,保证传输数据的完整性。为了解决传输线路的不稳定性造成数据包的丢失情况,tcp 使用了发送方超时重传和接收方数据应答的策略。概括而言,就是一种“状态协议“,保证通信双方数据收发的一致性。

TCP有限状态机

  1. TCP_CLOSE:关闭状态,一个新建的TCP socket 会处于该状态。
  2. TCP_LISTEN: 监听状态,一般服务器端套接字在调用Listen系统调用后即处于该状态。
  3. TCP_SYN_SENT:同步信号已经发送状态,这个状态一般是指客户端发送SYN(建立连接的同步)数据包后所处的状态(tcp三次握手的第一个包)。在接收到远端服务器端的应答后,即从该状态进入TCP_ESTABLISHED状态。
  4. TCP_SYN_RECEIVED:同步信号已经接受状态,服务器端在接受到远端客户端SYN数据包后,进行相应的处理(创建通信套接字等),然后发送应答数据包(tcp三次握手的第二个包),并将新创建的通信套接字状态设置为TCP_SYN_RECEIVED,在接受到客户端的应答后,即进入TCP_ESTABLISED状态。
  5. TCP_ESTABLISED:建立连接状态,这是双方进行正常通信所处的状态。
  6. TCP_FIN_WAIT_1:本地发送FIN(用于结束连接的)数据包后即可进入该状态,等待对方的应答。一般一端发送完其所要发送的数据后,即可发送FIN数据包,此时发送通道被关闭,但仍可继续接受远端发送的数据包。在接受到远端发送的对于FIN数据包的应答后,将进入TCP_FIN_WAIT_2状态。
  7. TCP_FIN_WAIT_2:进入该状态表示本地已经接受到远端发送的对于本地之前发送的FIN数据包的应答。进入该状态后,本地仍然可以继续接受远端发送给本地的数据包。在接受到远端发送的FIN数据包后(表示远端也已经发送完数据),本地将发送一个应答数据包,并进入TCP_TIME_WAIT状态。TCP_TIME_WAIT状态存在的时间被称为2MSL时间,这一方面是为避免本地发送的应答数据包丢失,另一方面避免一个新创建的套接字接收到旧套接字中遗留的数据包。
  8. TCP_TIME_WAIT:该转状态呗称为2MSL等待状态。如果在此期间接收到远端发送的FIN数据包,则表示之前在TCP_FIN_WAIT_2状态发送的ACK应答数据包在传输中丢失或者长时间被延迟,从而造成了远端重新发送了FIN数据包,此时重复ACK应答数据包。一旦2MSL时间到期,则将进入TCP_CLOSED状态,即完成关闭操作。
  9. TCP_CLOSE_WAIT:该状态存在于后关闭的一端。当接收到远端发送的FIN数据包后,本地发送一个ACK应答数据包,并将该套接字状态从TCP_ESTABLISED设置为TCP_CLOSE_WAIT。本地可以继续向远端发送数据包,在发送完所有的数据后,本地将发送一个FIN数据包关闭本地发送通道,并将状态设置为TCP_LAST_ACK状态,等待远端对FIN数据包的应答数据包。
  10. TCP_CLOSING:如果通信双方同时发送FIN数据包,则同时进行关闭操作,则双方将同时进入TCP_CLOSING状态。具体的,本地发送一个FIN数据包以结束本地数据包发送,如果在等待应答期间,接收到远端发送的FIN数据包,则本地将状态设置为TCP_CLOSING状态。在接收到应答后,再继续装入到TCP_CLOSE_WAIT状态。
  11. TCP_LAST_ACK:作为后关闭的一方,在发送FIN数据包后,即进入TCP_LAST_ACK状态。此时等待远端发送应答数据包,在接收到应答数据包后,即完成关闭操作,进入TCP_CLOSE状态。

三次握手

IP/TCP三次握手示意图

三次握手过程

三次握手过程是客户端主动向正在监听的服务发起交换序号、建立连接的过程,三次握手过程如下:

  1. 第一次握手
    客户端主动发送SYN包到服务器,其中包含客户端的初始序号seq=x,并进入SYN_SENT状态,等待服务器确认。(其中,SYN=1,ACK=0,表示这是一个TCP连接请求数据报文;序号seq=x,x是随机数,表明传输数据时的第一个数据字节的序号是x)。
  2. 第二次握手
    服务器收到请求后,必须确认客户的数据包,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态。(其中确认报文段中,标识位SYN=1,ACK=1,表示这是一个TCP连接响应数据报文,并含服务端的初始序号seq=y,y是随机数,以及服务器对客户端初始序号的确认号ack(服务器)=seq(客户端)+1=x+1)。
  3. 第三次握手
    客户端收到服务器的SYN+ACK包,向服务器发送确认包(seq=x+1,ack=y+1),此包发送完毕,客户端和服务器进入ESTAB_LISHED(TCP连接成功)状态,完成三次握手。

三次握手交换各自的序号,建立起连接。

握手过程为什么是三次而不是两次、四次?

TCP作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求!

为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤

两次握手的场景

两次握手过程其实只有三次握手的前两次握手,没有第三次握手

两次握手至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。

想象一个场景,client向server发送一个连接请求,由于一些原因,导致client发出的连接请求在一个网络节点逗留了比较多的时间。此时client会将此连接请求作为无效处理 又重新向server发起了一次新的连接请求,server正常收到此连接请求后建立了连接,数据传输完成后释放了连接。如果此时client发出的第一次请求又到达了server,server会以为client又发起了一次连接请求。如果是两次握手:此时连接就建立了,server会维持连接一直等待client发送数据,从而白白浪费server的资源。 如果是三次握手:由于client没有发起连接请求,也就不会理会server的连接响应,server没有收到client的确认连接,就会关闭掉本次连接。如果第一次握手大量延时或者第二次握手大量丢失 ,就会造成“SYN的洪水攻击”效果。

四次握手的场景

四次握手其实就是将第二次握手发送ACK与SYN分开进行,这样子有违高效的原则。

序列号与确认号

  • wireshark中序列号默认显示相对序列号,序列号是从0开始,实际序列号是在三次握手过程双方随机选取的。可以通过设置来显示真实的序列号,【编辑】->【首选项】->【Protocols】->【TCP】->【Relative sequence numbers(Requires “Analyer TCP sequence numbers”)】复选框去掉
  • 通过流量图来分析序列号与确认号。【统计】->【流量图】->【流类型】选择【TCP Flows】
    TCP Flows

通过HTTP请求的TCP流量图分析序列号与确认号

  1. 第一次握手,client发送SYN包请求建立TCP连接,初始化序列号seq = 4129057982,随机生成的, 确认号ack = 0,一般都是0;
  2. 第二次握手,server对请求进行确认,ack = 4129057982 + 1(SYN虽没负载数据,但消耗一个序列号),同时进行SYN,seq = 4200111240(随机)
  3. 第三次握手,client对server的SYN包发送ACK包,ack = 4200111240 + 1,seq = 4129057983
  4. 第四个包,client发送负载725字节的HTTP请求包,seq = 4129057983(注意ACK没有消耗seq序号,跟第三次握手的seq一样), ack = 4200111241
  5. 第五个包,server响应tcp确认包,seq = 4200111241, ack = 4129057983 + 725 = 4129058708

四次挥手

四次挥手主要是关闭tcp建立起来的连接,释放相关资源

四次挥手过程

在实际应用中,客户端主动关闭到服务器的连接,服务器在检测到客户端关闭连接后,关闭对应的连接。

  • 第一次挥手: 客户端发送一个FIN,用来关闭客户端到服务器的数据传送服务器的确认。其中终止标志位FIN=1,序列号seq=u.
  • 第二次挥手: 服务器收到这个FIN,它发送一个ACK,确认ack为收到的序号加一。
  • 第三次挥手: 关闭服务器到客户端的连接,发送一个FIN给客户端。
  • 第四次挥手: 客户端收到FIN后,并发回一个ACK报文确认,并将确认序号seq设置为收到序号加一。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

客户端发送FIN后,进入终止等待状态,服务器收到客户端连接释放报文段后,就立即给客户端发送确认,服务器就进入CLOSE_WAIT状态,此时TCP服务器进程就通知高层应用进程,因而从客户端到服务器的连接就释放了。此时是“半关闭状态”,即客户端不可以发送给服务器,服务器可以发送给客户端。
此时,如果服务器没有数据报发送给客户端,其应用程序就通知TCP释放连接,然后发送给客户端连接释放数据报,并等待确认。客户端发送确认后,进入TIME_WAIT状态,但是此时TCP连接还没有释放,然后经过等待计时器设置的2MSL后,才进入到CLOSED状态。

2.为什么需要2MSL时间?
首先,MSL即Maximum Segment Lifetime,就是最大报文生存时间,是任何报文在网络上的存在的最长时间,超过这个时间报文将被丢弃。《TCP/IP详解》中是这样描述的:MSL是任何报文段被丢弃前在网络内的最长时间。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒、1分钟、2分钟等。

TCP的TIME_WAIT需要等待2MSL,当TCP的一端发起主动关闭,三次挥手完成后发送第四次挥手的ACK包后就进入这个状态,等待2MSL时间主要目的是:防止最后一个ACK包对方没有收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可以继续使用。当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。

3.为什么是四次挥手,而不是三次或是五次、六次?
双方关闭连接要经过双方都同意。所以,首先是客服端给服务器发送FIN,要求关闭连接,服务器收到后会发送一个ACK进行确认。服务器然后再发送一个FIN,客户端发送ACK确认,并进入TIME_WAIT状态。等待2MSL后自动关闭。

总结:
(1)为了保证客户端发送的最后一个ACK报文段能够到达服务器。即最后一个确认报文可能丢失,服务器会超时重传,然后服务器发送FIN请求关闭连接,客户端发送ACK确认。一个来回是两个报文生命周期。

如果没有等待时间,发送完确认报文段就立即释放连接的话,服务器就无法重传,因此也就收不到确认,就无法按步骤进入CLOSED状态,即必须收到确认才能close。
(2)防止已经失效的连接请求报文出现在连接中。经过2MSL,在这个连续持续的时间内,产生的所有报文段就可以都从网络消失。

参考

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

callinglove

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值