TCP的三次握手四次挥手
TCP的三次握手和四次挥手不管是我们自己使用还是面试都是需要掌握的,本文先将原理,然后以三国为例讲个小栗子帮助理解。先来一张图:
标志位
TCP在其协议头中使用大量的标志位或者说1位(bit)布尔 域来控制连接状态,一个包中有可以设置多个标志位。
位码即TCP标志位,有6种标示:
- SYN(synchronous建立联机)
- 创建一个连接
- ACK(acknowledgement 确认)
- 确认接收到的数据
- FIN(finish结束)
- 终结一个链接
- PSH(push传送)
- RST(reset重置)
- URG(urgent紧急)
Initial Sequence Number(初始序列号)
ISN:是在建立TCP三次连接的时候,存储在序列号位置中的数字的代称。也就是说,告诉对方我将要开始发送的初始化序列号是多少,两边都要发这个ISN,即TCP三次连接中第一个SYN包和第二个SYN+ACK的包都有这个。
Sequence number(顺序号码)
seq的初始值在不同系统实现不一样,一般为随时间增长的值。当seq超过4字节存储空间后从0开始。
在某个方向上传输N个字节的数据,序列号就+N,因此seq用于确认在某个方向上传输的字节数。
如果传输的数据字节为0,即只有首部,那序列号还加吗?当syn或fin被置1,序列号也会+1。其他情况(如只有ack)不加。
Acknowledge number(确认号码)
只有ack标志置1才有效。在TCP交互的整个周期,除了syn包外其他所有包ack都被置1。
ack序列号是上次已经成功收到数据字节序号+1,比如上次成功接收了seq为1000的数据,发送的ack序号为1001,表示我seq1000以前的数据我已经成功接收了,我对序列号1001开始的数据感兴趣。
三次握手
TCP 是基于链接的,所以在传输数据前需要先建立链接,TCP 在传输上是双工传输,不区分 Client 端与 Server 端,为了便于理解,我们把主动发起建连请求的一端称作 Client 端,把被动建立链接的一端称作 Server 端。
简单理解
- 1.Client 发送链接请求
- 2.Server 收到并回应。
- 3.Client 收到后打开链接,并回应Server。Server接收到打开链接。
详解三次握手
首先建立链接前需要 Server 端先监听端口,因此 Server 端建立链接前的初始状态就是 LISTEN 状态。Client 端初始状态是CLOSED状态。
- 第一次握手:建立连接时,Client 端发送 SYN 包到 Server 端,并进入SYN_SEND状态,等待服务器确认;
- 首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。
- 第二次握手:Server 端收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时 Server 端进入 SYN_RECV 状态;
- 在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y.
- 第三次握手:
- Client 端收到 Server 端的 ACK 后,Client 端的链接状态就变成了 ESTABLISHED 状态。
- 同时 Client 端向 Server 端发送 ACK,回复 Server 端的 SYN 请求。Server 端收到 Client 端的 ACK 后,Server 端的链接状态也就变成了的 ESTABLISHED 状态。
- 在确认报文段中SYN=1,ACK=1,确认号ack=x+1。
此时建连完成,双方随时可以进行数据传输。
为什么要三次握手
三次握手是为了建立双向的链接。
那么问题来了,两次可以吗?
Server 端收到 Client 端的 SYN 请求后,发送了 ACK 和 SYN,但是 Client 端不进行回复,导致 Server 端大量的链接处在 SYN_RCVD 状态,进而影响其他正常请求的建连。这也是产生SYN洪水攻击的原因。
半连接队列:Server 端第一次收到 Client 端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
什么是 SYN 洪水攻击?
SYN 洪水(半开连接攻击)是一种拒绝服务 (DDoS) 攻击,旨在耗尽可用服务器资源,致使服务器无法传输合法流量。通过重复发送初始连接请求 (SYN) 数据包,攻击者将可击垮目标服务器计算机上的所有可用端口,导致目标设备在响应合法流量时表现迟钝乃至全无响应。
赤壁之战(起始篇)
赤壁之战:孙权、刘备联军抗曹。
- 第一次握手:刘备派诸葛亮去商量咱们联军偷袭曹操吧。
- 第二次握手:孙权收到诸葛亮传达的意思表示同意,并派遣周瑜找刘备约定时间咱们27号三更天去打曹操。
- 第三次握手:刘备收到诸葛亮传达孙权同意的消息,刘备就把军队准备好。同时呢,也收到周瑜的传话,并回应在27号三更天打曹操。
如果两次握手,刘备没做回应(或者消息被拦截),孙权在27日三更天去打曹操。结果刘备没去,那孙权是不是就完蛋了。
三次握手是为了确保刘备和孙权都准备好了并能保证按照约定时间去偷袭曹操。
四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,然后客户端主动关闭,服务器被动关闭。
简单理解
- 第一次挥手:Client 端数据发送完成告知 Server 端申请断连;
- 第二次挥手:Server 端收到断连并回应,Client 端继续等待最后数据的传送;
- 第三次挥手:Server 端业务完成再次发送回应消息并断开连接,Client 端收到回应;
- 第四次挥手:Client 端再次发送一次确认,并断开。
详解四次挥手
再来看看 TCP 的断连,如下图所示。
- 第一次挥手:Client 端发送一个 FIN 报文,报文中会指定一个序列号。停止再发送数据,主动关闭TCP连接,此时Client 端处于 FIN_WAIT1(终止等待1)状态,等待服务端的确认。
- Client 端发出连接释放报文段FIN=1,序号seq=u。
- 第二次挥手:
- Server 端收到 FIN 之后,会发送 ACK 报文,且把 Client 端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到 Client 端的报文了,此时 Server 端处于 CLOSE_WAIT(关闭等待)状态。
- 此时的TCP处于半关闭状态,Client 端到 Server 端的连接释放。Client 端收到 Server 端的确认后,进入FIN_WAIT2(终止等待2)状态,等待 Server 端发出的连接释放报文段。
- Server 端发出确认报文段ACK=1,确认号ack=u+1,序号seq=v。
- 第三次挥手:如果 Server 端也想断开连接了,和 Client 端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时 Server 端处于 LAST_ACK(关闭等待)状态,等待客户端的确认。
- Server 端发出连接释放报文段FIN=1,ACK=1,序号seq=w,确认号ack=u+1。
- 第四次挥手:Client 端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT(时间等待) 状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命) 的时间后,才进CLOSED(关闭等待)状态。服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
- Client 端发出确认报文段ACK=1,seq=u+1,ack=w+1
可以看到,服务器结束TCP连接的时间要比客户端早一些。
为什么要等待2MSL时间
原因有两个:
- 保证 TCP 协议的全双工连接能够可靠关闭,用来重发可能丢失的ACK报文。
- 保证这次连接的重复数据段从网络中消失,防止端口被重用时可能产生数据混淆。意思是说,其他时候的连接可能会被当作本次的连接。
赤壁之战(结束篇)
赤壁之战:孙权、刘备联军偷袭曹营,战争差不多了。
- 第一次挥手:刘备派赵云告诉孙权,咱们该走了,然后就等着孙权的答复。
- 第二次挥手:孙权收到赵云传达的意思表示知道了,赵云回去复命了,刘备就等着。
- 第三次挥手:孙权也觉得差不多完事了,就派程普告诉刘备咱们撤吧。
- 第四次挥手:刘备就回复,走吧,然后孙权就撤退了。为了防止有散兵留着,刘备又停留了一段时间才撤退。
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
无论是建连还是断链,都是需要在两个方向上进行。
建连时,Server 端的 SYN 和 ACK 合并为一次发送,
断链时,两个方向上数据发送停止的时间可能不同,所以不能合并发送 FIN 和 ACK。
这就是建连三次握手而断链需要四次的原因。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。