TCP 三次握手和四次挥手的问题在面试中是最为常见的考点之一. 很多读者都知道三次和四次, 但是如果问深入一点, 他们往往都无法作出准确回答.
本篇尝试使用动画来对这个知识点进行讲解, 期望读者们可以更加简单地地理解 TCP 交互的本质.
TCP/IP 代表传输控制协议 / 网际协议, 指的是一系列协组.
可分为四个层次:
1, 数据链路层, 网络层, 传输层和应用层.
2, 在网络层: 有 IP 协议, ICMP 协议, ARP 协议, RARP 协议和 BOOTP 协议.
3, 在传输层: 中有 TCP 协议与 UDP 协议.
4, 在应用层: 有 FTP,HTTP,TELNET,SMTP,DNS 等协议.
TCP 和 UDP 使用 IP 协议从一个网络传送数据包到另一个网络. 把 IP 想像成一种高速公路, 它允许其它协议在上面行驶并找到到其它电脑的出口. TCP 和 UDP 是高速公路上的 "卡车", 它们携带的货物就是像 HTTP, 文件传输协议 FTP 这样的协议等.
TCP 和 UDP 是 FTP,HTTP 和 SMTP 之类使用的传输层协议. 虽然 TCP 和 UDP 都是用来传输其他协议的, 它们却有一个显著的不同: TCP 提供有保证的数据传输, 而 UDP 不提供. 这意味着 TCP 有一个特殊的机制来确保数据安全的不出错的从一个端点传到另一个端点, 而 UDP 不提供任何这样的保证.
TCP 三次握手
TCP 三次握手就好比两个人在街上隔着 50 米看见了对方, 但是因为雾霾等原因不能 100% 确认, 所以要通过招手的方式相互确定对方是否认识自己.
相互拥抱
我们看到这个过程中一共是四个动作, 招手 - 点头微笑 - 招手 - 点头微笑. 其中连续进行了 2 个动作, 先是点头微笑(回复对方), 然后再次招手(寻求确认), 实际上可以将这两个动作合一, 招手的同时点头和微笑(syn+ack). 于是四个动作就简化成了三个动作, 招手 - 点头微笑并招手 - 点头微笑. 这就是三次握手的本质, 中间的一次动作是两个动作的合并.
我们看到有两个中间状态, syn_sent 和 syn_rcvd, 这两个状态叫着「半打开」状态, 就是向对方招手了, 但是还没来得及看到对方的点头微笑. syn_sent 是主动打开方的「半打开」状态, syn_rcvd 是被动打开方的「半打开」状态. 客户端是主动打开方, 服务器是被动打开方.
- syn_sent: syn package has been sent
- syn_rcvd: syn package has been received
TCP 数据传输
TCP 数据传输就是两个人隔空对话, 差了一点距离, 所以需要对方反复确认听见了自己的话.
客户端喊了一句话(data), 接收方听见了之后要回复自己听见了(ack).
如果喊了一句, 半天没听到对方回复, 就认为自己的话被大风吹走了, 没听见, 所以需要重新喊话, 这就是 tcp 重传.
也有可能是服务端听到了客户端的话, 但是 Server 向 Client 的回复被大风吹走了, 以至于 Client 没听见 Server 的回复. Client 并不能判断究竟是自己的话被大风吹走了还是 Server 的回复被大风吹走了, Client 也不用管, 重传一下就是.
Client 可以向 Server 喊话, 同样 Server 也可以向 Client 喊话, 因为 tcp 链接是「双工的」, 双方都可以主动发起数据传输. 不过无论是哪方喊话, 都需要收到对方的确认才能认为对方收到了自己的喊话.
Client 可能是个高射炮, 一说连说了八句话, 这时候 Server 可以不用一句一句回复, 而是连续听了这八句话之后, 一起向对方回复说前面你说的八句话我都听见了, 这就是批量 ack. 但是 Client 也不能一次性说了太多话, Server 的脑子短时间可能无法消化太多, 两人之间需要有协商好的合适的发送和接受速率, 这个就是「TCP 窗口大小」.
TCP 三次连接总结
(1****) 第一次握手: 建立连接时, 客户端 A 发送 SYN 包 (SYN=j) 到服务器 B, 并进入 SYN_SEND 状态, 等待服务器 B 确认.
(2) 第二次握手: 服务器 B 收到 SYN 包, 必须确认客户 A 的 SYN(ACK=j+1), 同时自己也发送一个 SYN 包(SYN=k), 即 SYN+ACK 包, 此时服务器 B 进入 SYN_RECV 状态.
(3) 第三次握手: 客户端 A 收到服务器 B 的 SYN+ACK 包, 向服务器 B 发送确认包 ACK(ACK=k+1), 此包发送完毕, 客户端 A 和服务器 B 进入 ESTABLISHED 状态, 完成三次握手.
完成三次握手, 客户端与服务器开始传送数据.
TCP 四次挥手
TCP 断开链接的过程和建立链接的过程比较类似, 只不过中间的两部并不总是会合成一步走, 所以它分成了 4 个动作, Client 挥手(fin)--Server 伤感地微笑(ack)--Server 挥手(fin)--Client 伤感地微笑(ack).
之所以中间的两个动作没有合并, 是因为 tcp 存在「半关闭」状态, 也就是单向关闭. Client 已经挥了手, 可是人还没有走, 只是不再说话, 但是耳朵还是可以继续听, Server 呢继续喊话. 等待 Server 累了, 也不再说话了, 超 Client 挥了挥手, Client 伤感地微笑了一下, 才彻底结束了.
image
上面有一个非常特殊的状态 time_wait, 它是主动关闭的一方在回复完对方的挥手后进入的一个长期状态, 这个状态标准的持续时间是 4 分钟, 4 分钟后才会进入到 closed 状态, 释放套接字资源. 不过在具体实现上这个时间是可以调整的.
它就好比主动分手方要承担的责任, 是你提出的要分手, 你得付出代价. 这个后果就是持续 4 分钟的 time_wait 状态, 不能释放套接字资源 (端口), 就好比守寡期, 这段时间内套接字资源(端口) 不得回收利用.
它的作用是重传最后一个 ack 报文, 确保对方可以收到. 因为如果对方没有收到 ack 的话, 会重传 fin 报文, 处于 time_wait 状态的套接字会立即向对方重发 ack 报文.
同时在这段时间内, 该链接在对话期间于网际路由上产生的残留报文 (因为路径过于崎岖, 数据报文走的时间太长, 重传的报文都收到了, 原始报文还在路上) 传过来时, 都会被立即丢弃掉. 4 分钟的时间足以使得这些残留报文彻底消逝. 不然当新的端口被重复利用时, 这些残留报文可能会干扰新的链接.
4 分钟就是 2 个 MSL, 每个 MSL 是 2 分钟. MSL 就是 maximium segment lifetime-- 最长报文寿命. 这个时间是由官方 RFC 协议规定的. 至于为什么是 2 个 MSL 而不是 1 个 MSL, 我还没有看到一个非常满意的解释.
四次挥手也并不总是四次挥手, 中间的两个动作有时候是可以合并一起进行的, 这个时候就成了三次挥手, 主动关闭方就会从 fin_wait_1 状态直接进入到 time_wait 状态, 跳过了 fin_wait_2 状态.
TCP 四次分手总结
由于 TCP 连接是全双工的, 因此每个方向都必须单独进行关闭. 这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接. 收到一个 FIN 只意味着这一方向上没有数据流动, 一个 TCP 连接在收到一个 FIN 后仍能发送数据. 首先进行关闭的一方将执行主动关闭, 而另一方执行被动关闭.
客户端 A 发送一个 FIN, 用来关闭客户 A 到服务器 B 的数据传送(报文段 4).
服务器 B 收到这个 FIN, 它发回一个 ACK, 确认序号为收到的序号加 1(报文段 5). 和 SYN 一样, 一个 FIN 将占用一个序号.
服务器 B 关闭与客户端 A 的连接, 发送一个 FIN 给客户端 A(报文段 6).
客户端 A 发回 ACK 报文确认, 并将确认序号设置为收到序号加 1(报文段 7).
总结
来源: http://www.jianshu.com/p/b26876184a59