首先需要明确的是 TCP 是一个可靠传输协议;它的所有特点最终都是为了这个可靠传输服务。在网上看到过很多文章讲 TCP 连接的三次握手和断开连接的四次挥手;但是都太过于理论;看完感觉总是似懂非懂。反复思考过后;觉得我自己还是偏工程型的人;要学习这些理论性的知识;最好的方式还是要通过实际案例来理解;这样才会具象深刻。本文通过 Wireshark 抓包来分析 TCP 三次握手和四次挥手;如果你也对这些理论感觉似懂非懂;那么强烈建议你也结合抓包实践来强化理解这些理论性的知识。
TCP 建立连接的三次握手是连接的双方协商确认一些信息;Sequence number、Maximum Segment Size、Window Size 等;;Sequence number 有两个作用;一个是 SYN 标识位为 1 时作为初始序列号;ISN;;则实际第一个数据字节的序列号和相应 ACK 中的确认号就是这个序列号加 1;另一个是 SYN 标识位为 0 时;则是当前会话的 segment;传输层叫 segment;网络层叫 packet;数据链路层叫 frame;的第一个数据字节的累积序列号。Maximum Segment Size 简称 MSS;表示最大一个 segment 中能传输的信息;不含 TCP、IP 头部;。Window Size 表示发送方接收窗口的大小。
图中三个小红框表示与服务器建立连接的三次握手。
第一步;client 端;这个示例也就是浏览器;发送 SYN 到 server 端;
第二步;server 端收到 SYN 消息后;回复 SYN ; ACK 到client 端;ACK 表示已经收到了 client 的 SYN 消息;
第三步;client 端收到回复 SYN ; ACK 后;也回复一个 ACK 表示收到了 server 端的 SYN ; ACK 了;其实
到这一步;client 端的 60469 端口已经是 ESTABLISHED 状态了。 可以看到;其实三次握手的核心目的就是双方互相告知对象自己的 Sequence number;蓝框是 client 端的初始 Sequence number 和 client 端回复的 ACK;绿框是 server 端的初始 Sequence number 和 client 端回复的 ACK。这样协商好初始 Sequence number 后;发送数据包时发送端就可以判断丢包和进行丢包重传了。
三次握手还有一个目的是协商一些信息;上图中黄色方框是 Maximum Segment Size;粉色方框是 Window Size;。
到这里;就可以知道平常所说的建立TCP连接本质是为了实现 TCP 可靠传输做的前置准备工作;实际上物理层并没有这个连接在那里。TCP 建立连接之后时拥有和维护一些状态信息;这个状态信息就包含了 Sequence number、MSS、Window Size 等;TCP 握手就是协商出来这些初始值。而这些状态才是我们平时所说的 TCP 连接的本质。因为这个太重要了;我还要再次强调一下;TCP 是一个可靠传输协议;它的所有特点最终都是为了这个可靠传输服务。
C/C;;Linux服务器开发高级架构师/C;;后台开发架构师免费学习地址
【文章福利】另外还整理一些C;;后台开发架构师 相关学习资料;面试题;教学视频;以及学习路线图;免费分享有需要的可以自行添加;Q群;720209036 点击加入~ 群文件共享
下面再来看看;当关闭浏览器页面是发生断开连接的四次挥手过程:
相信你已经发现了;上图抓包抓到的不是四次挥手;而是三次挥手;这是为何呢?
这是由于 TCP 的时延机制;因为系统内核并不知道应用能不能立即关闭;;当被挥手端;这里是 server 的 443 端口;第一次收到挥手端;这里是 client 的 63612 端口;的 FIN 请求时;并不会立即发送 ACK;而是会经过一段延迟时间后再发送;但是此时被挥手端也没有数据发送;就会向挥手端发送 FIN 请求;这里就可能造成被挥手端发送的 FIN 与 ACK 一起被挥手端收到;导致出现第二、三次挥手合并为一次的现象;也就最终呈现出“三次挥手”的情况。
断开连接四次挥手分为如下四步;假设没有出现挥手合并的情况;;
第一步;client 端主动发送 FIN 包给 server 端;
第二步;server 端回复 ACK;对应第一步 FIN 包的 ACK;给 client;表示 server 知道 client 端要断开了;
第三步;server 端发送 FIN 包给 client 端;表示 server 端也没有数据要发送了;可以断开了;
第四步;client 端回复 ACK 包给 server 端;表示既然双发都已发送 FIN 包表示可以断开;那么就真的断开了啊。
下面是 TCP 连接流转状态图;其中 CLOSED 状态是虚拟的;实际上并不存在;;这个图很重要;记住这个图后基本上所有的 TCP 网络问题就可以解决。
其中比较难以理解的是 TIME_WAIT 状态;主动关闭的那一端会经历这个状态。这一端停留在这个状态的最长时间是 Maximum segment lifetime;MSL;的 2 倍;大部分时候被简称之为 2MSL。存在 TIME_WAIT 状态有如下两个原因;
要可靠的实现 TCP 全双工连接终止;
让老的重复 segment 在网络中消失;一个 sement 在网络中存活的最长时间为 1 个 MSL;一来一回就是 2 MSL;;
嘿嘿;这是个经典的面试题;其实大部分人都背过挥手是四次的原因;因为 TCP 是全双工;双向;的;所以回收需要四次......。但是再反问下;握手也是双向的;但是为什么是只要三次呢?
网上流传的资料都说 TCP 是双向的;所以回收需要四次;但是握手也是双向;握手双方都在告知对方自己的初始 Sequence number;;那么为什么就不用四次握手呢?所以凡事需要多问几个为什么;要有探索和怀疑精神。
你再仔细回看上面三次握手的第二步;SYN ; ACK;;其实是可以拆分为两步的;第一步回复 ACK;第二步再发 SYN 也是完全可以的;只是效率会比较低;这样的话三次握手不也变成四次握手了。
看起来四次挥手主要是收到第一个 FIN 包后单独回复了一个 ACK 包这里多了一次;如果能像握手那样也回复 FIN ; ACK 那么四次挥手也就变成三次了。这里再贴一下上面这个挥手的抓包图;
这个图中第二个红框就是 server 端回复的 FIN ; ACK 包;这样四次挥手变成三次了;如果一个包算一次的话;。这里使用四次挥手原因主要是;被动关闭端在收到 FIN 后;知道主动关闭端要关闭了;然后系统内核层会通知应用层要关闭;此时应用层可能还需要做些关闭前的准备工作;可能还有数据没发送完;所以系统内核先回复一个 ACK 包;然后等应用层准备好了主动调 close 关闭时再发 FIN 包。
而握手过程中就没有这个准备过程了;所以可以立即发送 SYN ; ACK;在这里的两步合成一步了;提高效率;。挥手过程中系统内核在收到对方的 FIN 后;只能 ACK;不能主动替应用来 FIN;因为系统内核并不知道应用能不能立即关闭。
TCP 是一个很复杂的协议;为了实现可靠传输以及处理各种网络传输中的 N 多问题;有一些很经典的解决方案;比如其中的网络拥塞控制算法、滑动窗口、数据重传等。强烈建议你去读一下 rfc793 和 TCP/IP 详解 卷1;协议 这本书。
如果你是那些纯看理论就能掌握好一门技能;然后还能举三反一的人;那我很佩服你;如果不是;那么学习理论知识注意要结合实践来强化理解理论;要经过反反复复才能比较好地掌握一个知识;讲究技巧;必要时要学会通过工具来达到目的。
最后 TCP 所有特性基本上核心都是为了实现可靠传输这个目标来服务的;然后有一些是出于优化性能的目的。
原文链接;https://juejin.cn/post/7162815000434704415