
三次握手与四次挥手交换序号、验证全双工与最小成本排雷我的github(https://github.com/xcx55/ubuntu-linux-project)感谢各位大佬参观我的github源笔记TCP三次握手四次挥手浅层描述26-9-25、TCP性质126-9-26、TCP函数时序26-9-26报头字段认识完了这一篇回答网络面试的两大常青题为什么握手是三次、挥手是四次但比背答案更重要的是笔记里的追问方式——握手到底在验证什么为什么不是两次、不是四次把这层想通答案自己长出来。一、先摆正一件事握手挥手到底在干什么三次握手的目的是为了建立连接、建立信道。而阻碍正常通信的情况有三种双方意愿对方愿不愿意连网络问题通不通双方交换序号——这才是关键TCP 一切机制的前提是序号而序号的起始值ISN随机起始编号必须双方在连接时交换并确认。所以三次握手干了三件事1、交换了信息ISN 随机起始编号2、保证了在各自立场上双方 IO 全双工都是完整的3、验证了网络当前起码是好的一句话总结三次本质是为了交换序号的稳定性——顺带验证网络和全双工。二、为什么必须是三次两次为什么不行一次根本传不了两次无法确认双方都拿到了对方的序号也无法验证全双工——客户端知道自己能发、能收收到服务端的回复但服务端只知道自己能发不知道自己发出去的对方收没收到。第三次的真正意义第三次握手报文客户端→服务端的 ACK发出并成功服务端才认为连接建立——因为这一刻它才确认自己的出是通的对方的入也是通的。细节补一刀要是第三次报文丢了服务端会再次发出 ACKSYN直到得到 ACK——收到这个重复应答服务端逻辑上就知道了至少目前网络是通畅的而且客户端的写端没有问题。三次握手证明的是S、C 两端的读写都没有问题。四次为什么不行不是不可以是不划算逻辑上 4 次以上都可以5、6 次更稳但4 次握手本质可以但是没有这么做——代价太大。想达到同样效果2 次不够、4 次多了3 次刚刚好以最小成本验证网络、建立连接、初步排雷而且主流操作系统内核里4 次握手的路径直接被禁止了——取而代之的做法是直接断开客户端的连接请求比如服务端忙不过来时拒绝而不是慢慢握四次。三次握手本质是四次异步的合并看穿这一层握手挥手就统一了三次握手的本质依旧是四次握手——只不过服务端把 ACK 和 SYN 合并成一次发同步的。四次握手是异步的特例先把 ACK 发回去过一会再单独发 SYN——效果等价于三次握手但主流内核没有这么实现的。一般客户端请求连接服务端都是愿意的、立即回复的所以 ACKSYN 天然合并在一个报文里——三次成了常态。三、四次挥手为什么断开比建立多一次断开连接和建立连接的区别在于意愿建立连接时服务器基本会统一回复不会说你先等一会我先回你 ACK、过会再给你 SYN断开连接一般是一方一厢情愿——你说我要断对面可能说“不行我还有数据没发完。你自己把写端关了可以但我的写端还要保留你的读端也得留着”这就是著名的半关闭主动断方关写端、留读端被动方还得把剩下的数据发完才发自己的 FIN。读和写是两条线、两个方向、异步进行的——所以是四次。三次挥手真的存在三次挥手不是段子当双方恰好同时都要断写端你断、我也断两个 FIN 还能捎带合并四次变三次——这是同步的、少见的、代价不大、收益为正所以内核接受它。而四次握手异步、少见、代价大、收益为负被内核拒绝。三次四次都是不同情况的产物——这些是大佬们最终测定后的规律。四、握手挥手也可能失败本质上还是连线的过程即使握手挥手报文依旧可能发送失败——会重复发送导致三次握手变成 n3 次、挥手变成 n4 次。TCP 对此的态度还是那个字重传。连接建立的过程本质就是两条线读线、写线逐条验证连通的过程每条线都可能失败、都可能重试。五、超时重传等不到应答怎么办发送方视角收到了应答我 100% 确定上一个报文送到了没收到不管是数据丢还是应答丢都不能干等。所以发送方有一个特定时间间隔用于判定丢包——间隔怎么定网络好间隔短一点网络不好间隔长一点动态计算经典策略500ms × 2^n 的等待时间——每重传一次等待翻倍指数退避超时重传必然带来重复报文——所以又回到 seq 序号去重。顺带引出后面要学的两个状态TIME_WAIT、CLOSE_WAIT——为什么一个端口号用完释放后短时间内不能再 bind答案就藏在挥手之后的 TIME_WAIT 里。六、accept 和 connect都不参与三次握手对着函数时序看一个经典误区被澄清// 服务端 // 客户端socket();socket();bind();connect();listen();accept();服务端 accept 干了什么——不参与三次握手它只是等待三次握手完成把已经连接好的连接获取上来connect 也不参与三次握手——它只负责发起三次握手。握手是两台主机的内核TCP 协议栈之间的事应用层的函数只是在合适的时机发起、或在完成后收尸。这也呼应了前面的世界观传输层的活在内核里干应用层只是用户态的门面。总结三次握手三件事交换 ISN 序号关键前提、验证全双工、初步排雷——序号是 TCP 一切机制的前提没交换成功就没有一致性两次不够无法确认双方序号全双工、四次代价大被内核禁止拒绝请求取而代之——三次是最小成本解第三次报文丢了 → 服务端重发 ACKSYN收到即证明网络通、客户端写端通四次挥手源于半关闭断开是一方一厢情愿另一方还有数据要发读写两条线异步各断各的三次挥手真实存在双方同时断、FIN 合并少见但内核接受握手挥手本质是两条线的连通验证失败照样重传超时重传 动态间隔500ms×2^n seq 去重accept 不参与握手只取连接connect 不参与握手只发起——握手是内核间的事。下一篇半连接队列和全连接队列的本质以及为什么是 TIME_WAIT 让端口冷却。