最近的校招的笔试和面试经常被问到Tcp的三次握手,虽然之前对于三次握手和四次挥手有了解,但是一直没有深入研究,抽出一点时间来将整个TCP的连接和建立进行深入的研究。
最开始记住三次握手还是看到知乎上一个段子:
A:我能给你讲一个tcp的笑话吗?
B:行,你给我讲一个tcp的笑话吧。
A:好吧,那我给你讲一个tcp的笑话”
然后,我就光记住了这个段子 : )
一:为什么需要有三次握手?
我们直到TCP协议是面向连接的,和UDP不同,在传输数据之前会先进行连接的建立。而TCP的连接建立是采用客户端(Client)和服务端(Server)的模式进行的,Client是主动建立连接的应用进程,Server是被动等待连接建立的应用进程。而”三次握手”就是TCP连接建立的过程。
二:三次握手的具体过程?
我们结合图开始进行详解。
首先Client和Server端都处于CLOSED状态,当需要建立连接的时候,Client端主动打开连接,Server端被动打开连接。
1.Client向Server发送请求连接的报文,报文首部SYN=1,seq=x
2.Server接受到Client的请求报文后,向Client发送确认连接的报文,此时将ACK置为1,并且ack(确认号)=x+1 ,并且消耗自己的一个序列号 seq=y
(ps:TCP报文中,当双方同意建立连接的时候ACK应该置为1,只有ACK=1的时候,确认号才有效,当ACK=0表示这是一个请求连接的报文)
3.Client收到Server的确认后会再次向Server发送一次确认报文
当三次握手完成之后,Client和Server端口才进入ESTAB-LISHED状态,可以进行数据的传送
对于不熟悉的TCP报文首部的人来说,可能会对这里的ACK,seq,ack以及相应的数值产生疑问,这里解释一下相应的报文首部字段
seq:序号,当前报文段发送的数据的第一个字节的序号
ack:确认号,表示期望对方下一个报文段的第一个字节数据的序号
ACK(ACKnowlegement):确认,只有当ACK=1的时候确认号才生效,当来连接建立后或者双方同意建立连接的时候应该将ACK置为1,可以看成一个标志位
但是在第二步收到Server的确认之后,还是需要向Server发送一个确认报文呢?
假设A向B发了写了一封信,但是由于某些原因,A的信在路上延误了很久,在这期间A又搬家了,之后B才收到了这封信,此时B仍认为A的地址不变,于是向A回了一封信。如果没有第三次握手,也就是B不需要接受到A的确认就开始向A邮寄礼物,那么就会造成资源的浪费。
三.什么又是四次挥手?
TCP连接建立之后,当完成了任务之后,需要进行连接的释放,四次挥手就是连接释放的过程。整个过程可以这样比喻:
A:我已经没有数据发送给你了,我想释放连接
B:我已经确认你想要释放连接了,但是还需要等我的数据给你发送完毕
…….
B向A的数据发送完毕
B:我的数据发送完毕了,可以释放连接了
A:我已经确认了,可以释放连接了
四.四次挥手的过程?
1.当数据传送完毕后,Client处于ESTAB-LISHED阶段,发送连接释放报文给Server,并且停止发送数据,状态转换为FIN-WAIT-1
2.Server接受到Client的连接释放报文之后,状态转换为CLOSE-WAIT,Server仍然能够向Client进行数据传送,处于”半关闭”状态,并且向Client发送确认报文
3.当Server端数据传送完毕,向Client发送连接释放报文,进入LAST-ACK状态
4.Client接受到Server端的连接释放报文之后,向Server发送确认报文
由此可以看出四次挥手可以看成是两个二次挥手,只是发送者和接受者身份互换。
为什么Client接收到连接释放报文,向Server发送确认报文还需要等待2MSL?
1.Client发送的报文可能会丢失,丢失后Server会向Client发送重传请求,如果没有2MSL的等待时间,就不能保证Client发送的所有数据Server都接受到了
2.为了清空无效的连接请求
这里介绍的是Client端主动断开连接,还有一种情况是Client和Server端同时断开连接。
Case 3: both users close simultaneously
A simultaneous CLOSE by users at both ends of a connection causes
FIN segments to be exchanged. When all segments preceding the FINs
have been processed and acknowledged, each TCP can ACK the FIN it
has received. Both will, upon receiving these ACKs, delete the
connection.
当双方同时断开连接,会同时发送连接释放请求,此时的连接释放状态可以用下同表示
TCP A TCP B
1. ESTABLISHED ESTABLISHED
2. (Close) (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> ... FIN-WAIT-1
<-- <SEQ=300><ACK=100><CTL=FIN,ACK> <--
... <SEQ=100><ACK=300><CTL=FIN,ACK> -->
3. CLOSING --> <SEQ=101><ACK=301><CTL=ACK> ... CLOSING
<-- <SEQ=301><ACK=101><CTL=ACK> <--
... <SEQ=101><ACK=301><CTL=ACK> -->
4. TIME-WAIT TIME-WAIT
(2 MSL) (2 MSL)
CLOSED CLOSED
那么一定需要经历四次挥手吗?
可以看到在第一次Client端向Server发送连接释放请求的时候,Server立即响应并且发送了ACK(确认)报文之后,之后数据传送结束了才会Server端会再次发送连接释放报文(FIN),那么Server端可不可以将ACK报文和FIN报文合成同一个报文一起发送给Client,只进行”三次挥手”呢?