为啥要三次握手与四次挥手
相比于UDP, TCP是有连接的, 这个连接就体现在这了.
三次握手就是TCP建立连接, 四次挥手就是TCP断开连接.
三次握手
握手是指通信双方进行网络交互.
三次握手就相当于是 客户端与服务器间通过三次交互来建立连接关系.
(建立连接关系, 本质就是双方记录对方信息)
要了解三次握手, 首先我们还得来了解 syn报文 与 ack报文.
syn 与 ack
syn 称为同步报文段, 意思是一方要向另一方申请建立连接.
ack 是确认字符, 表示发来的数据已经接收无误.
报文具体结构是怎样的呢?
什么样的报文是 syn 报文呢?
观察TCP报头结构, 我们可以看到在 16为窗口大小 左边有六个特殊比特位, 这些比特位默认为0, 如果为 1, 则表示其特殊含义.
比如, 第二位是 ack, 如果这一位为 1, 则表示当前数据报是一个应答报文.
同样的, 第五位是 syn, 如果为 1, 则表示当前数据报是一个同步报文.
如果一个TCP数据报, 第二位和第五位都是1, 那么就是 ack + syn 报文.
三次握手具体流程
如下图 :
- 客户端请求同步, 也就是发送 syn.
- 服务器接收到 syn, 就需要进行应答, 就发送了 ack, 同时也要与客户端请求同步, 就发送了 syn, 因为是同时发生的, 所以就包含在了一个数据报中, 也就是一次握手了.
- 客户端接收到数据报后, 也要返回一个响应, 表示收到数据报, 就返回 ack.
其实三次握手就相当于投石问路, 测试客户端与服务器是否具有发送与接收数据的能力.
这是后续可靠传输的基础.
我们可以类比成两个人在网上进行语音通话 :
当甲乙双方连上麦后, 就得测试一下是否有问题.
首先甲说一句 : “听得到我说话吗?”
乙听到后就得响应甲, 说 : “听到了, 你听到我说话吗?” (这里乙就知道了, 甲的发送能力没问题, 乙的接收能力也没问题)
甲听到乙说话后, 就再回一句 : “听到了” (这里甲就知道了, 乙的接收发送能力没问题, 甲的接收发送也没问题)
最后就是乙收到甲的消息, 明白了甲的接收能力没问题, 乙的发送也没问题.
至此, 甲乙双方都测试出了两方的发送与接收没问题.
注意 :
上述过程是内核自动完成, 应用程序干预不了.
等到三次握手完成后, 就建立连接了, 服务器的 accept 把建立好的连接从内核拿到应用程序中.
四次挥手
就是双方断开连接, 来看下图 :
- 客户端向服务器发送 fin(结束报文), 通知服务器 “我要与你断开连接了”.
- 服务器收到后, 就返回 ack, 就是响应服务器的通知, 相当于 “我知道了”.
- 服务器执行到了 clos 方法后也发送通知, 说 “我也要与你断开连接了”.
- 客户端收到后返回 ack 响应, 相当于说 “好的”.
我们这情况很类似与三次握手, 是否可以把四次挥手变为三次挥手呢? (将ack与fin和并)
其实, ack 与 fin 是有一定的概率和并的, 但通常情况下不能和并.
为什么三次握手的第二次握手能和并, 而这里就不行呢?
三次握手, ack 和 syn 都是在同一时机触发的 (都是内核来完成的)
四次挥手, ack 与 fin 的触发时机不同.
ack 是内核完成的, 在收到 fin 的第一时间会返回.
fin 则是应用程序代码控制的, 在调用 socket 的 close 的方法的时候才会返回 fin.
如果趁着 ack 还没发就 close, 则可以进行和并.
其实客户端之所以发送 fin, 也是因为客户端进程执行到了 close 方法(即使代码中没有调用 close 方法, 在进程结束时也会自动进行执行 close), 释放资源.
此时客户端进程虽然结束了, 但是 tcp 连接还在, 内核还是会把 tcp 连接继续维护, 直到四次挥手完成. 服务这边也是一样.
注意
建立连接, 一定是客户端先发起.
断开连接, 双方都有可能先发起.