TCP 建立/断开连接
三次握手
在建立 TCP 连接之前,客户端和服务器之间会发送三次数据,以确认双方的接收和发送能力,这个过程称为三次握手(Three-way Handshake)。
三次握手的具体过程如下所示。
第一次握手:刚开始客户端处于 CLOSED 的状态,服务端处于 LISTEN 状态。客户端给服务端发送一个 SYN 报文,并指明客户端的初始化序列号 ISN,此时客户端处于 SYN_SEND 状态。
第二次握手:当服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也指定了自己的初始化序列号 ISN。同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
第三次握手:当客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也同样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方成功建立起了连接。
为什么建立连接的时候需要进行三次握手
分别看看每次握手的目的就能知道了。第一次握手成功让服务端知道了客户端具有发送能力,第二次握手成功让客户端知道了服务端具有接收和发送能力,但此时服务端并不知道客户端是否接收到了自己发送的消息,所以第三次握手就起到了这个作用。经过三次通信后,服务端和客户端都确认了双方的接收和发送能力。
为什么不是两次
根本原因: 无法确认客户端的接收能力。
分析如下:
如果是两次,你现在发了 SYN 报文想握手,但是这个包滞留在了当前的网络中迟迟没有到达,TCP 以为这是丢了包,于是重传,两次握手建立好了连接。
看似没有问题,但是连接关闭后,如果这个滞留在网路中的包到达了服务端呢?这时候由于是两次握手,服务端只要接收到然后发送相应的数据包,就默认建立连接,但是现在客户端已经断开了。
看到问题的吧,这就带来了连接资源的浪费
三次握手的目的是确认双方发送和接收的能力,那四次握手可以嘛?
当然可以,100 次都可以。但为了解决问题,三次就足够了,再多用处就不大了。
第三次握手的时候,可以携带。前两次握手不能携带数据。
如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的 SYN 报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险。
第三次握手的时候,客户端已经处于 ESTABLISHED 状态,并且已经能够确认服务器的接收、发送能力正常,这个时候相对安全了,可以携带数据。
如果双方同时发 SYN 报文,状态变化会是怎样的呢?
这是一个可能会发生的情况。
在发送方给接收方发 SYN 报文的同时,接收方也给发送方发 SYN 报文,两个人刚上了!
发完 SYN,两者的状态都变为 SYN-SENT。
在各自收到对方的 SYN 后,两者状态都变为 SYN-REVD。
接着会回复对应的 ACK + SYN,这个报文在对方接收之后,两者状态一起变为 ESTABLISHED。
这就是同时打开情况下的状态变迁。
四次挥手
当客户端和服务端断开连接时要发送四次数据,这个过程称之为四次挥手
第一次挥手:在挥手之前服务端与客户端都处于 ESTABLISHED 状态。客户端发送一个 FIN 报文,用来关闭客户端到服务器的数据传输,此时客户端处于 FIN_WAIT_1 状态。
第二次挥手:当服务端收到 FIN 之后,会发送 ACK 报文,并且把客户端的序列号值加 1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
第三次挥手:如果服务端同意关闭连接,则会向客户端发送一个 FIN 报文,并且指定一个序列号,此时服务端处于 LAST_ACK 的状态。
第四次挥手:当客户端收到 ACK 之后,处于 FIN_WAIT_2 状态。待收到 FIN 报文时发送一个 ACK 报文作为应答,并且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。等待一段时间后会进入 CLOSED 状态,当服务端收到 ACK 报文之后,也会变为 CLOSED 状态,此时连接正式关闭。
注意了,这个时候,客户端需要等待足够长的时间,具体来说,是 2 个 MSL(Maximum Segment Lifetime,报文最大生存时间), 在这段时间内如果客户端没有收到服务端的重发请求,那么表示 ACK 成功到达,挥手结束,否则客户端重发 ACK
如果不等待会怎样?
如果不等待,客户端直接跑路,当服务端还有很多数据包要给客户端发,且还在路上的时候,若客户端的端口此时刚好被新的应用占用,那么就接收到了无用数据包,造成数据包混乱。所以,最保险的做法是等服务器发来的数据包都死翘翘再启动新的应用。
那,照这样说一个 MSL 不就不够了吗,为什么要等待 2 MSL?
1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端 1 个 MSL 确保对端没有收到 ACK 重传的 FIN 报文可以到达
这就是等待 2MSL 的意义。
为什么建立连接只通信了三次,而断开连接却用了四次
因为当服务端收到客户端的 FIN 报文后,发送的 ACK 报文只是用来应答的,并不表示服务端也希望立即关闭连接。
当只有服务端把所有的报文都发送完了,才会发送 FIN 报文,告诉客户端可以断开连接了,因此在断开连接时需要四次挥手。
TCP 和 UDP 的区别
首先概括一下基本的区别:
TCP 是一个面向连接的、可靠的、基于字节流的传输层协议。
而 UDP 是一个面向无连接的传输层协议。(就这么简单,其它 TCP 的特性也就没有了)。
具体来分析,和 UDP 相比,TCP 有三大核心特性:
-
面向连接。所谓的连接,指的是客户端和服务器的连接,在双方互相通信之前,TCP 需要三次握手建立连接,而 UDP 没有相应建立连接的过程。
-
可靠性。TCP 花了非常多的功夫保证连接的可靠,这个可靠性体现在哪些方面呢?一个是有状态,另一个是可控制。
TCP 会精准记录哪些数据发送了,哪些数据被对方接收了,哪些没有被接收到,而且保证数据包按序到达,不允许半点差错。这是有状态。
当意识到丢包了或者网络环境不佳,TCP 会根据具体情况调整自己的行为,控制自己的发送速度或者重发。这是可控制。
相应的,UDP 就是无状态, 不可控的。
- 面向字节流。UDP 的数据传输是基于数据报的,这是因为仅仅只是继承了 IP 层的特性,而 TCP 为了维护状态,将一个个 IP 包变成了字节流。