目录
- 名词解释
TCP的三次握手
- TCP建立链接的步骤
- TCP的三次握手步骤
- 思考:TCP握手为什么不是两次 or 四次?
TCP的四次挥手
- TCP断开链接的步骤
- TCP的四次挥手步骤
- 思考:为什么断开链接的时候要多一个步骤2呢?
- 思考:为什么最后客户端确认断开链接之后还要等待2WSL呢?
- 面试题:TCP为什么是3次握手,4次挥手?
这是一个计算机网络中一个很热门,很基础的问题,也是面试常考的一个题,如果你会那不稀奇,如果你不会,那就会凉凉。我这里来对我学的东西做一个整理,看完时候对这里的知识应该会很清晰。首先先来名词解释,如果遇到不清晰的名词,记得反过头来看。
名词解释
TCP
:TCP
在计算机网络模型的传输层,对应的是主机到主机的传输,为应用间通信提供能力。TCP
是一个双工协议,数据任何时候都可以双向传输,这就意味着客户端和服务端可以平等地发送、接收信息。- 链接:是传输层的概念,链接是一种传输数据的行为,是网络行为状态的记录。在传输之前,建立一个链接,就是在数据收发双方的内存中都建立一个用于维护数据传输状态的对象,里面记录了双方的
IP
和端口号,状态是怎样,传输速度是如何等。 - 双工/单工:
名称 | 概念 | 线路数量 |
---|---|---|
单工 | 在任何一个时刻,如果数据只能单向发送 | 只需1条( =1 ) |
半双工 | 在某个时刻数据可以向一个方向传输,也可以向另一个方向反方向传输,而且交替进行 | 至少 1 条( >= 1 ) |
全双工 | 任何时刻数据都可以双向收发 | 大于 1 条( >1 ) |
- 握手:
TCP
的握手目的是为了建立稳定的链接通道。 - 挥手:
TCP
的挥手目的是为了断开链接。 WSL(Maximum Segment Lifetime)
:报文最大生存时间,TCP允许不同的实现可以设置不同的WSL值。seq
序号:用来标识从TCP
源端向目的端发送的字节流,发起方发送数据时对此进行标记📌。ack
确定序号:只有ACK
标志为1时,确认序号字段才有效,ack = seq + 1
标志位:
SYN(Synchronization)
:一个Host
主动向另一个Host
发起连接,请求同步。ACK(Acknowledgement)
:因为要保持连接和可靠性约束,TCP
协议要保证每一条发出的数据必须给返回。接收方收到数据后,都需要给发送方一个响应确认序号有序。RST(Reset)
:重置链接FIN(Finish)
:一个Host
主动断开请求,请求完成PSH(Push)
:一个Host
给另一个Host
发送数据,数据推送- ...
TCP的三次握手
TCP
的三次握手,相对来说是一个比较完整的机制,旨在建立稳定的传输通道。
TCP建立链接的步骤
下面来看一下TCP
建立链接的6个步骤:
- 客户端发消息给服务端
(SYN)
- 服务端准备好进行连接
- 服务端针对客户端的
(SYN)
给一个(ACK)
- 服务端发送一个
(SYN)
给客户端 - 客户端准备就绪
- 客户端给服务端发送一个
(ACK)
其中,2和5步骤是不需要进行握手的,3和4是可以合并到一起的。所以虽然建立链接要6步但是只需要三次握手,分别是1,3+4,6。
TCP的三次握手步骤
下面我们把三次握手的过程还原一下:
- 开始客户端和服务端都处于
CLOSED
的状态,客户端发送SYN=1
给服务端表示要求建立链接,并且发送了一个seq
序号,这个时候客户端的状态变成SYN-SEND
。 - 服务端收到消息返回一个
ACK=1
表示确认收到,还有ack确定序号,是上一个seq
序号+1
,即x+1
,还有本次的seq
序号y
,还有一个SYN=1
建立链接,这个时候服务端状态变成SYN-REVD
。 - 客户端收到消息之后向服务端发送一个
ACK=1
表示确认收到,还有一个新的seq
序号是x+1
,还有一个ack
确定序号是上一个seq
序号+1
,即y+1
。完成之后客户端的状态编程ESTABLISHED
。 - 服务端接收到消息之后状态也变成
ESTABLISHED
,链接通道建立。
简要总结就是:
- 客户端 -> SYN -> 服务端
- 客户端 <- SYN+ACK <- 服务端
- 客户端 -> ACK -> 服务端
思考:TCP握手为什么不是两次 or 四次?
设想一下,如果只有两次,服务端还没有确定客户端是否准备好了,这样是无法稳定的进行数据传输的。如果四次,服务端根据客户端的ACK
再给客户端回复一个ACK
,没有什么很大的作用还造成资源浪费,那就是很没有必要的事情了。三次正好既可以保证可靠传输,也可以提高传输效率,
TCP的四次挥手
TCP
的挥手旨在把链接状态断开
TCP断开链接的步骤
- 客户端发消息要求断开链接
(FIN)
。 - 服务端接收到请求后,会先对自己是否收到请求回应
(ACK)
。 - 服务端处理剩余的事情,例如服务端还有没有发送完的消息,服务端可能还有发送出去的消息没有得到
ACK
;也有可能服务端自己有资源要释放等。 - 服务端处理完自己的事情,告诉客户端可以关闭链接了
(FIN)
。 - 客户端收到
FIN
,处理自己完成的事情,比如客户端有发送给服务端没有收到ACK
的请求等。 - 客户端处理完成自己的事情,告诉服务端可以关闭
(ACK)
。
其中3和5是不需要进行挥手的,但是注意这里,2和4是无法合并的。所以这里需要四次挥手,分别是1,2,4,6。
TCP的四次挥手步骤
- 开始客户端向服务端发送
FIN=1
要断开链接,并且发送了一个seq
序号=u
,这个时候客户端变成FIN-WAIT1
。 - 服务端接收到消息之后,返回一个
ACK=1
确定消息,还有ack
确认序号,是上一个seq
序号+1
,即u+1
,还有一个新的seq
序号为v
,此时服务端的状态变成CLOSE-WAIT
,客户端收到消息之后,状态会变成FIN-WAIT2
。 - 等服务端准备好所有的东西可以关闭链接的时候,向客户端发送
ACK=1
,还要发送ack
确认序号,上一个seq
序号+1
,即u+1
,还有一个新的seq
序号为w
,还要发送一个FIN=1
。如果有之前没有发送完的数据,会跟着这次请求一并发送给客户端。此时服务端的状态变成LASE-ACK
。 - 客户端收到消息之后,把自己这里的东西都完成向服务端发送
ACK=1
确认消息,还发送了ack
确认序号,上一个seq
序号+1
,即w+1
,还有一个新的seq
序号为u+1
,然后客户端的状态就变成了TIME-WAIT
,这种状态会持续2WSL
,如果等待的这段时间不再收到后端的消息,2WSL
之后会变成CLOSED
。服务端接收到消息之后,状态也变成CLOSED
。
简要总结就是:
- 客户端 -> FIN -> 服务端
- 客户端 <- ACK <- 服务端
- 客户端 <- FIN <- 服务端
- 客户端 -> ACK -> 服务端
思考:为什么断开链接的时候要多一个步骤2呢?
因为断开链接服务端接收到FIN
时,断开连接要处理的问题比较多,不能直接关闭链接,这个时候如果不发送ACK
回应说是内容收到了,客户端是无法判断这个消息服务端收到没有,不能让客户端在这种情况下等太久,所以应该先回复一个ACK
报文说是收到了,你等会我这里还有事情要处理。等到服务端的事情都处理完毕了,再发送一个FIN
,确定可以断开链接了。
思考:为什么最后客户端确认断开链接之后还要等待2WSL呢?
- 第一用于保证客户端发送的最后一个
ACK
报文可以到达服务器,如果这个ACK
报文丢失,服务器会觉得客户端没有收到我发的请求断开报文,于是服务器就会重发一次,如果客户端在这个2MSL
时间段内收到重传的报文,就会重新给出回应报文,还会重启2MSL
计时器。 - 第二是在这个
2WSL
时间中,可以使本链接持续时间内产生的所有报文段从网络中消失,这样新的TCP
三次握手的时候就不会出现旧链接中失效的请求报文。
面试题:TCP为什么是3次握手,4次挥手?
TCP
在传输层,对应主机到主机的传输,为应用通信提供能力,且TCP
是一个双工协议,为了保证双方都建立稳定而高效的数据传输,使用三次握手和四次挥手的工作机制。
三次握手的步骤是:
- 客户端向服务端发送
SYN
建立链接的请求(大哥,能建立链接吗?)
- 服务端要将
ACK+SYN
打包为一条消息回复(老妹儿,我收到你的消息了,可以进行链接,你收到了吗?)
- 客户端接收到消息之后给服务端发送
ACK
作为回复(好嘞,走起~)
,这个时候数据传输通道建立。
为什么不是两次,是因为客户端不返回ACK
,那么服务端不知道客户端有没有接收到消息,如果是四次,根据ACK
再返回一次ACK
,浪费带宽且没有必要。
四次挥手的步骤是:
- 客户端向服务端发送
FIN
断开链接的请求(大哥,我这边东西都发完了,断开链接吧~)
- 服务端接收到信息,给客户端发送一个
ACK
进行回复(老妹儿?要断开链接?我知道了,你稍等会儿啊)
- 服务端处理完需要处理的事情,返回
FIN
给客户端(我这边完事儿了,断开链接吧~)
- 客户端收到消息,处理完自己的事情,返回
ACK
给服务端(好嘞,掰掰~)
,这个时候数据传输通道断开。
为什么这里是四次,是因为断开链接服务端收到FIN
的时候,还有一些事情要处理,需要一些时间,这个时候不能让客户端等太久,所以先回复一个ACK
表示消息已经收到了,这边有东西要处理稍微等一下。等到事情处理完,再给客户端发送FIN同意断开链接。
其实这个很好理解,在生活中,我们收到对方发送的一个文件或者一个视频,这个时候他们需要我们根据内容进行回复或者点评。我们应该先说一句内容收到了,我看看给你回复,这样不会让对方有疑问半天没有回复是没有收到还是收到了正在看。等我们看完之后再告诉对方他们要的结果。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。