很多朋友第一次接触TCP,不是从RFC文档开始的,而是从面试题开始的:三次握手、四次挥手到底是怎么回事?
网上讲这个的文章一搜一大把,但大部分套路是把流程图甩出来,把标志位列一遍,让你背下来。看完当下觉得懂了,过几天再被问,又只能磕磕绊绊挤出几个词:SYN、ACK、FIN。
我做过几年网络相关开发,也帮人排查过不少连接异常问题,越来越有一个感受:这三个词不是需要死记的规定,而是一套很自然的逻辑。你只要想明白TCP为什么存在、所谓“可靠传输”到底靠什么维持,三次握手和四次挥手完全可以现场推出来。而且你推出来的过程,和标准答案几乎一样。
这篇内容适合所有被TCP折腾过的人。不管你是后端开发、客户端开发、嵌入式开发,还是正在准备面试,把这件事想透之后,再碰到连接超时、端口占用、CLOSE_WAIT堆积、TIME_WAIT过高,至少能知道问题大概卡在哪个环节。
我会从TCP到底在解决什么问题开始,把三次握手、四次挥手逐包拆开讲,最后再带你在本机用抓包工具实际看一遍完整过程。
1. 先绕开一个误区:TCP“连接”不是真的连了一根线
1.1 IP网络的“尽力而为”,逼出了TCP的可靠性
这是一个很容易被忽略的背景:TCP是建立在IP网络之上的。而IP网络本身的承诺非常有限,用专业一点的话说叫“尽力而为”。
所谓尽力而为,就是说网络上的路由器会尽量帮你把包送过去,但不做任何保证。包可能丢失,可能会延迟很久才到,可能会乱序到达,甚至同一个包可能在网络里被复制多份。物理链路断没断、对端关机没有,IP层本身并不知道。
如果直接在IP层上传数据,发送方把一段话拆成很多个IP包扔出去,接收方收到的可能是残缺的、乱序的、重复的内容。没人帮你排序,也没人帮你确认有没有缺东西。
TCP就是为这件事而生的。它要在不可靠的IP网络上,给应用程序提供一个看起来“可靠”的字节流通道。发送方知道数据有没有被对方收到,接收方知道数据有没有缺漏,双方还要有能力把乱序的包重新排好。
1.2 所谓“连接”,其实是双方各自维护的一组状态
很多人一听“建立TCP连接”,脑海里会出现一根从客户端拉到服务端的网线。这个画面是错的。
实际上,TCP连接不是一条存在于物理世界里的独立线路。客户端到服务端之间,包该怎么走还是怎么走,路径甚至可能每次都不一样。那连接到底在哪?
连接在双方的操作系统内核里。
当一个TCP连接建立后,客户端和服务端的内核里各自维护着一份关于这个连接的状态信息,包括双方的IP和端口、当前收发数据的序列号、已经确认到哪个字节了、接收窗口还剩多少
