# 【NO.582】如何能够看懂TCP/IP 协议细节？

## **1.高门槛，勿入**

在Cisco平台上有一个很有用的Traceback log功能，实时记录当前Code运行到特定模块、特定函数，这样一旦系统崩溃、死机的时候，可以有效记录机器是在哪个模块、哪个函数里崩溃的。只要将此Traceback输入解析器，就可以定位是哪个模块出了问题，提交Bug的时候就可以Assign给负责此模块的Team，避免部门之间的推诿。

其实Traceback的原理也很简单，即每进入一个模块，就记录一下实时位置，类似于Debug。TCP/IP协议如果也拥有类似的traceback日志信息，那么只要用packet触发一下，就可以实时看一个packet如何在TCP/IP内部函数流转，在packet被递交给网卡之前，打印出每一个函数的名称，然后进入TCP/IP源码，按照函数调用的先后次序（流水线）依次进入阅读，只有这样才能详细了解TCP/IP细节。但是这样的学习门槛很高，源码也不是那么可以轻易看懂。对于大多数读者来说，更容易的方法是先易后难，在开始TCP/IP学习之前，先问自己一个问题，为什么要有TCP/IP协议？

## **2.计算机网络的标准件**

TCP/IP是计算机网络世界的标准件，这些标准件类似于汽车界的标准轮胎、汽车稳定系统ESP，即使看似牛叉的Tesla汽车生产商，也不是所有汽车零部件都是自己生产。除了电池、外壳、软件是自己生产的除外，其他的标准件全部是采购而来。通过使用标准件，平均只要几分钟即可生产一辆汽车。如果不使用标准件而全部采用自研，那生产的周期要大大加长。

既然TCP/IP是标准件，是不是也要像汽车轮胎一样提供标准的接口？否则怎么安装呢？是的，TCP/IP提供了诸多的标准接口，通过这些标准接口可以有效使用标准件，如DNS、TCP、UDP、IP、ICMP、IGMP、ARP、DHCP等等标准件。当然如果一一介绍这些标准件如何使用，这些标准件内部的工作原理是非常枯燥的，还是以小例子看看这些标准件是如何协作的。

## **3.一个小例子**

在浏览里输入知乎的网址（[http://zhihu.com](http://zhihu.com/)），然后输入回车，浏览器使用的TCP/IP第一个标准件是什么呢？

读者说，浏览器与知乎服务器通信使用http，而http通常是使用标准件TCP的，那么第一个标准件应该是TCP，对吗？不对。TCP这个标准件，对于http来说身份是服务员，尽管TCP身份低微，但是却有鲜明个性。就是**任何人差遣自己干活，必须使用IP地址**。浏览器不能使用网址（[http://zhihu.com](http://zhihu.com/)）这个字符串来差遣自己，罢工。

## **4.标准件之DNS**

所以，浏览器使用第一个TCP/IP标准件是DNS。DNS其实就是将知乎的网址（[http://zhihu.com](http://zhihu.com/)）解析成一个IP地址。有了知乎的IP地址，才能使用TCP这个标准件。

读者会说，DNS域名查询，会使用UDP这个标准件，UDP标准件会使用IP标准件，IP标准件直接会使用以太网卡标准件，对吗？不对。以太网卡标准件，作为IP标准件的服务员，尽管身份卑微，但是个性鲜明，凡是让自己干活的客人，必须提供接收方的MAC地址，否则恕不接待。所以IP标准件需要先使用ARP标准件，查询接收方的MAC地址。读者又说，ARP这个标准件使用的也是以太网卡标准件，没有接收方的MAC地址，以太网卡不是恕不接待的嘛？是的。但是ARP使用广播MAC地址（FFFFFFFFFFFF）来查询啊，所以以太网卡不得不执行查询任务啊。假设一切顺利，ARP得到了回复答案，将ARP答案返回IP标准件。既然IP标准件拥有了接收方的MAC地址，就可以使用以太网卡标准件，将packet提交给以太网卡，packet的进一步处理就和TCP/IP没有关系了。不一会DNS查询结果报文返回，按照时间先后次序进入网卡物理层、MAC层、IP标准件、UDP标准件、DNS标准件，由DNS标准件将查询结果告知浏览器。以上的DNS查询过程，浏览器其实只直接使用了DNS标准件，并没有直接使用其它标准件，其他标准件的使用都是由DNS标准件（进程）间接触发使用。有读者会说，DNS标准件、ARP标准件功能非常清晰也很有必要，多出来的那两个标准件IP、UDP看起来非常多余，为什么要有它们啊？

## **5.标准件之IP**

问读者一个问题，浏览器查询得到知乎的IP地址，是留给自己看的吗？

很显然不是，IP地址肯定是给互联网看的，准确地说是给互联网上的IP路由器看的，IP路由器看了知乎的IP地址，才能将IP报文快递到知乎服务器，不是吗？**知乎的IP地址写在什么地方呢？**写在packet的IP报文头。**由谁来写**？IP标准件，IP标准件所做的工作远不是在包裹的外层添加一个IP封装，并在外包装上写下知乎的IP地址（目的IP）、浏览器主机的IP地址（源IP）那么简单，还需要查询路由表，寻找合适的出口。否则一个主机有多个网卡，连接内网和互联网，主机怎么将访问知乎的packet发给连接互联网的网卡，而不是连接内网的网卡呢？这就是IP标准件的另外一项主要工作。而要完成种种复杂的工作，最好的方式就是以一个实例instance形式（进程）存在。至于UDP标准件，先不写，等写完TCP再写。

## **6.标准件之TCP**

再返回第一个问题，浏览器拥有了知乎的IP地址，是否就可以将http request用TCP标准件发出了呢？

依然不行。TCP这个怪癖狂，非常有个性，要想使用它必须遵守它的怪癖。这个怪癖是什么呢？**Create（）**需要先要调用TCP标准件一个Create指定，该指令其实就是在本地内存创建一个结构体（内存），用于缓存TCP连接所有相关的变量、数据，最最重要的是这个结构体有一个全局唯一的ID，浏览器以后要与TCP标准件沟通，只能使用这个唯一的ID（FD）。Okay，TCP结构体创建成功，是不是就可以发送http request了？依然不行。**Connect（）**TCP需要先建立连接，因为上文的结构体是空白的，需要建立连接将关键参数进行初始化，双方的ISN，MSS、Window Size、SACK、Scaling Window等等。建立TCP连接，使用TCP标准件的connect接口，填入合适的参数，最重要的是知乎IP地址以及知乎端口，是80还是443，这将决定接下来是否需要使用TLS标准件，前者不需要，后者需要。这就是大名鼎鼎的TCP三次握手，其实三次握手一点也不准确。真实的含义是三个packet完成连接，浏览器发2个packet，知乎服务器发1个packet。之所以一直就这样以讹传讹到现在也没有纠正，是因为英中翻译这样最顺口。需要指出的是，TCP尽管有很多怪癖，但是connect环节以及之后的环节还是非常友好的。你只要调用一次connect即可，至于浏览器是发2个packet连接成功，还是多次重传连接成功，这都是connect接口内部的实现，浏览器无需关心。Connect只会告知浏览器两个结果，成功或者失败。读者有点急不可耐了，TCP连接建立成功，http request是否就可以发送出去了？**Send（）**如果浏览器连接的是知乎TCP 端口80，是的，浏览器需要将http request数据通过调用TCP标准件send（）接口发送出去，浏览器调用接口时需要携带FD唯一ID。如果浏览器连接的是知乎TCP 端口443，即https服务，依然不能发送数据。浏览器需要使用TLS标准件，与知乎服务器完成TLS安全加密连接才能发送http request。

## **7.标准件之TLS**

尽管TLS是一个标准件，但是它却工作于TCP之上，即TLS依赖于TCP为自己提供服务。所以TLS不是传统意义上的TCP/IP标准件。

TLS安全连接，使用上文创建的唯一文件标识符FD，使用TCP标准件的Send（）、Receive（）完成双向数据的收发，当浏览器认证知乎服务器数字证书合法双方协商出加密/解密参数，至此TLS标准件通知安全连接创建成功或者失败。浏览器的http request终于可以发出了，先流经TLS标准件，添加TLS 报文头，数据加密+数据完整性保护。再流转到TCP标准件，添加TCP报文头并发出，等待对方确认，启动超时重传定时器。再流转到IP标准件，查路由找出口，添加IP报文头。最后到达网卡，使用IP标准件提供的参数，完成以太网封装，进入发送队列。一旦轮到该报文，由物理层添加Preamble + SFD + CRC将信号发送出去。上文标准件调用的过程中，最值得研究的就是TCP，只有它对来自浏览器的http request进行了数据缓存，并启动了定时器。假设第一次发出的http request在互联网被丢了，对该http request重传的，既不是浏览器、也不是TLS、也不是IP、更不是以太网卡，而是那个有诸多怪癖的TCP，通过多次重传以应对可能丢包对浏览器数据的影响，这也是TCP可靠性的真正含义。浏览器一梭子打出去之后就不管数据死活了，TCP接管数据的重传重任。

## **8.标准件之UDP**

最后说到UDP，看了上文的TCP发数据的过程，是不是觉得很繁琐？是的，不光繁琐而且非常耗时，如果连DNS查询也要使用TCP传输，意味着用户需要等待更长的时间才能开始传输真正的数据，需要用户有更长久的延迟满足，很显然耐心不够的用户会直接放弃。

而有了UDP就没有那么繁琐了，用户使用UDP发送数据数据，无需等待、无需建立连接，直接嗖得一声，数据就UDP、IP、以太网走起，紧凑整洁，节省了用户大量时间。比如基于IP的实时语音以及视频，为了良好的用户体验，通常需要50ms收发一个包裹，要到达这个低延迟，使用TCP很显然是无法完成的，剩下的唯一选择就是使用UDP来承载。TCP/IP做为计算机网络标准件的集中营，每一个标准件都可以直接调用，而不全是上文的间接调用。比如用户可以直接使用IP标准件、ICMP标准件写程序，甚至直接调用以太网卡标准件，而不仅仅局限于TCP、UDP、DNS、TLS。

原文地址：https://zhuanlan.zhihu.com/p/608306304

作者：linux