# 【NO.161】深入理解TCP/IP 协议栈

TCP/IP 协议栈是一系列网络协议的总和，是构成网络通信的核心骨架，它定义了电子设备如何连入因特网，以及数据如何在它们之间进行传输。TCP/IP 协议采用4层结构，分别是**应用层、传输层、网络层和链路层**，每一层都呼叫它的下一层所提供的协议来完成自己的需求。由于我们大部分时间都工作在应用层，下层的事情不用我们操心；其次网络协议体系本身就很复杂庞大，入门门槛高，因此很难搞清楚TCP/IP的工作原理，通俗一点讲就是，**一个主机的数据要经过哪些过程才能发送到对方的主机上**。 接下来，我们就来探索一下这个过程。

## **1.物理介质**

物理介质就是把电脑连接起来的物理手段，常见的有光纤、双绞线，以及无线电波，它决定了电信号(0和1)的传输方式，物理介质的不同决定了电信号的传输带宽、速率、传输距离以及抗干扰性等等。

TCP/IP协议栈分为四层，每一层都由特定的协议与对方进行通信，而**协议之间的通信最终都要转化为 0 和 1 的电信号，通过物理介质进行传输才能到达对方的电脑**，因此物理介质是网络通信的基石。

下面我们通过一张图先来大概了解一下TCP/IP协议的基本框架：

![img](https://pic4.zhimg.com/80/v2-d61906b4feb0889e3732dd29570008ab_720w.webp)

当通过http发起一个请求时，应用层、传输层、网络层和链路层的相关协议依次对该请求进行包装并携带对应的**首部**，最终在链路层生成**以太网数据包**，以太网数据包通过物理介质传输给对方主机，对方接收到数据包以后，然后再一层一层采用对应的协议进行拆包，最后把应用层数据交给应用程序处理。

网络通信就好比送快递，商品外面的一层层包裹就是各种协议，协议包含了商品信息、收货地址、收件人、联系方式等，然后还需要配送车、配送站、快递员，商品才能最终到达用户手中。

一般情况下，快递是不能直达的，需要先转发到对应的配送站，然后由配送站再进行派件。

配送车就是物理介质，配送站就是网关， 快递员就是路由器，收货地址就是IP地址，联系方式就是MAC地址。

快递员负责把包裹转发到各个配送站，配送站根据收获地址里的省市区，确认是否需要继续转发到其他配送站，当包裹到达了目标配送站以后，配送站再根据联系方式找到收件人进行派件。

有了整体概念以后，下面我们详细了解一下各层的分工。

1、链路层

网络通信就是把有特定意义的数据通过物理介质传送给对方，单纯的发送 0 和 1 是没有意义的，要传输有意义的数据，就需要以字节为单位对 0 和 1 进行分组，并且要标识好每一组电信号的信息特征，然后按照分组的顺序依次发送。以太网规定一组电信号就是一个数据包，一个数据包被称为**一帧，** 制定这个规则的协议就是**以太网协议**。一个完整的以太网数据包如下图所示：

![img](https://pic3.zhimg.com/80/v2-18f378bb56a46f06da5c5eeafb5fac76_720w.webp)

整个数据帧由**首部**、**数据**和**尾部**三部分组成，首部固定为14个字节，包含了目标MAC地址、源MAC地址和类型；数据最短为46个字节，最长为1500个字节，如果需要传输的数据很长，就必须分割成多个帧进行发送；尾部固定为4个字节，表示数据帧校验序列，用于确定数据包在传输过程中是否损坏。因此，以太网协议通过对电信号进行分组并形成数据帧，然后通过物理介质把数据帧发送给接收方。那么以太网如何来识接收方的身份呢？

以太网规协议定，接入网络的设备都必须安装网络适配器，即**网卡，** 数据包必须是从一块网卡传送到另一块网卡。而**网卡地址**就是数据包的发送地址和接收地址，也就是帧首部所包含的**MAC地址，**MAC地址是每块网卡的身份标识，就如同我们身份证上的身份证号码，具有全球唯一性。MAC地址采用十六进制标识，共6个字节， 前三个字节是厂商编号，后三个字节是网卡流水号，例如**4C-0F-6E-12-D2-19**

有了MAC地址以后，以太网采用**广播**形式，把数据包发给该**子网内**所有主机，子网内每台主机在接收到这个包以后，都会读取首部里的**目标MAC地址**，然后和自己的MAC地址进行对比，如果相同就做下一步处理，如果不同，就丢弃这个包。

所以链路层的主要工作就是**对电信号进行分组并形成具有特定意义的数据帧，然后以广播的形式通过物理介质发送给接收方。**

## **2.网络层**

对于上面的过程，有几个细节问题值得我们思考：

发送者如何知道接收者的MAC地址？

发送者如何知道接收者和自己同属一个子网？

如果接收者和自己不在同一个子网，数据包如何发给对方？

为了解决这些问题，网络层引入了三个协议，分别是**IP协议**、**ARP协议**、**路由协议。**

【1】IP协议

通过前面的介绍我们知道，MAC地址只与厂商有关，与所处的网络无关，所以无法通过MAC地址来判断两台主机是否属于同一个子网。

因此，网络层引入了IP协议，制定了一套新地址，使得我们能够区分两台主机是否同属一个网络，这套地址就是网络地址，也就是所谓的**IP地址。**

IP地址目前有两个版本，分别是**IPv4**和**IPv6**，IPv4是一个32位的地址，常采用4个十进制数字表示。IP协议将这个32位的地址分为两部分，前面部分代表网络地址，后面部分表示该主机在局域网中的地址。由于各类地址的分法不尽相同，以C类地址**192.168.24.1**为例**，**其中前24位就是网络地址，后8位就是主机地址。因此，**如果两个IP地址在同一个子网内，则网络地址一定相同。**为了判断IP地址中的网络地址，IP协议还引入了**子网掩码，**IP地址和子网掩码通过**按位与**运算后就可以得到网络地址。

由于发送者和接收者的IP地址是已知的(应用层的协议会传入)， 因此我们只要通过子网掩码对两个IP地址进行AND运算后就能够判断双方是否在同一个子网了。

【2】ARP协议

即地址解析协议，是根据**IP地址**获取**MAC地址**的一个网络层协议。其工作原理如下：

ARP首先会发起一个请求数据包，数据包的首部包含了目标主机的IP地址，然后这个数据包会在链路层进行再次包装，生成**以太网数据包，**最终由以太网广播给子网内的所有主机，每一台主机都会接收到这个数据包，并取出标头里的IP地址，然后和自己的IP地址进行比较，如果相同就返回自己的MAC地址，如果不同就丢弃该数据包。ARP接收返回消息，以此确定目标机的MAC地址；与此同时，ARP还会将返回的MAC地址与对应的IP地址存入本机ARP缓存中并保留一定时间，下次请求时直接查询ARP缓存以节约资源。cmd输入 arp -a 就可以查询本机缓存的ARP数据。

【3】路由协议

通过ARP协议的工作原理可以发现，**ARP的MAC寻址还是局限在同一个子网中**，因此网络层引入了路由协议，首先通过IP协议来判断两台主机是否在同一个子网中，如果在同一个子网，就通过ARP协议查询对应的MAC地址，然后以广播的形式向该子网内的主机发送数据包；如果不在同一个子网，以太网会将该数据包转发给本子网的**网关**进行路由。网关是互联网上子网与子网之间的桥梁，所以网关会进行多次转发，最终将该数据包转发到目标IP所在的子网中，然后再通过ARP获取目标机MAC，最终也是通过广播形式将数据包发送给接收方。

而完成这个路由协议的物理设备就是**路由器，**在错综复杂的网络世界里，路由器扮演者**交通枢纽**的角色，它会根据信道情况，选择并设定路由，以最佳路径来转发数据包。

【4】IP数据包

在网络层被包装的数据包就叫**IP数据包，**IPv4数据包的结构如下图所示：

![img](https://pic3.zhimg.com/80/v2-31fd5c1561cc7370a075d05390aa3812_720w.webp)

IP数据包由首部和数据两部分组成，首部长度为20个字节，主要包含了目标IP地址和源IP地址，目标IP地址是网关路由的线索和依据；数据部分的最大长度为65515字节，理论上一个IP数据包的总长度可以达到65535个字节，而以太网数据包的最大长度是1500个字符，如果超过这个大小，就需要对IP数据包进行分割，分成多帧发送。

所以，网络层的主要工作是**定义网络地址，区分网段，子网内MAC寻址，对于不同子网的数据包进行路由。**

## 3.传输层

链路层定义了主机的身份，即MAC地址， 而网络层定义了IP地址，明确了主机所在的网段，有了这两个地址，数据包就从可以从一个主机发送到另一台主机。但实际上数据包是从一个主机的某个应用程序发出，然后由对方主机的应用程序接收。而每台电脑都有可能同时运行着很多个应用程序，所以当数据包被发送到主机上以后，是无法确定哪个应用程序要接收这个包。

因此传输层引入了**UDP协议**来解决这个问题，为了给每个应用程序标识身份，UDP协议定义了**端口**，同一个主机上的每个应用程序都需要指定唯一的端口号，并且规定网络中传输的数据包必须加上端口信息。 这样，当数据包到达主机以后，就可以根据端口号找到对应的应用程序了。UDP定义的数据包就叫做UDP数据包，结构如下所示：

![img](https://pic1.zhimg.com/80/v2-35c9a3a99fbc8409861d99a64c2d1964_720w.webp)

UDP数据包由首部和数据两部分组成，首部长度为8个字节，主要包括源端口和目标端口；数据最大为65527个字节，整个数据包的长度最大可达到65535个字节。

UDP协议比较简单，实现容易，但它没有确认机制， 数据包一旦发出，无法知道对方是否收到，因此可靠性较差，为了解决这个问题，提高网络可靠性，**TCP协议**就诞生了，TCP即传输控制协议，是一种面向连接的、可靠的、基于字节流的通信协议。简单来说**TCP就是有确认机制的UDP协议，**每发出一个数据包都要求确认，如果有一个数据包丢失，就收不到确认，发送方就必须重发这个数据包。

为了保证传输的可靠性，TCP 协议在 UDP 基础之上建立了**三次对话**的确认机制，也就是说，在正式收发数据前，必须和对方建立可靠的连接。由于建立过程较为复杂，我们在这里做一个形象的描述：

主机A：我想发数据给你，可以么？

主机B：可以，你什么时候发？

主机A：我马上发，你接着！

经过三次对话之后，主机A才会向主机B发送正式数据，而UDP是面向非连接的协议，它不与对方建立连接，而是直接就把数据包发过去了。所以 TCP 能够保证数据包在传输过程中不被丢失，但美好的事物必然是要付出代价的，相比 UDP，TCP 实现过程复杂，消耗连接资源多，传输速度慢。

TCP 数据包和 UDP 一样，都是由首部和数据两部分组成，唯一不同的是，TCP 数据包没有长度限制，理论上可以无限长，但是为了保证网络的效率，通常 TCP 数据包的长度不会超过IP数据包的长度，以确保单个 TCP 数据包不必再分割。

总结一下，传输层的主要工作是**定义端口，标识应用程序身份，实现端口到端口的通信，TCP协议可以保证数据传输的可靠性**。

## 4.应用层

理论上讲，有了以上三层协议的支持，数据已经可以从一个主机上的应用程序传输到另一台主机的应用程序了，但此时传过来的数据是字节流，不能很好的被程序识别，操作性差。因此，应用层定义了各种各样的协议来规范数据格式，常见的有 HTTP、FTP、SMTP 等，HTTP 是一种比较常用的应用层协议，主要用于B/S架构之间的数据通信，其报文格式如下：

![img](https://pic2.zhimg.com/80/v2-31ec165ae9364b9e95b899b2d4df08a1_720w.webp)

在 Resquest Headers 中，Accept 表示客户端期望接收的数据格式，而 ContentType 则表示客户端发送的数据格式；在 Response Headers 中，ContentType 表示服务端响应的数据格式，这里定义的格式，一般是和 Resquest Headers 中 Accept 定义的格式是一致的。

有了这个规范以后，服务端收到请求以后，就能正确的解析客户端发来的数据，当请求处理完以后，再按照客户端要求的格式返回，客户端收到结果后，按照服务端返回的格式进行解析。

所以应用层的主要工作就是**定义数据格式并按照对应的格式解读数据。**

## 5.全流程

首先我们梳理一下每层模型的职责：

**链路层**：对0和1进行分组，定义数据帧，确认主机的物理地址，传输数据；

**网络层**：定义IP地址，确认主机所在的网络位置，并通过IP进行MAC寻址，对外网数据包进行路由转发；

**传输层**：定义端口，确认主机上应用程序的身份，并将数据包交给对应的应用程序；

**应用层**：定义数据格式，并按照对应的格式解读数据。

然后再把每层模型的职责串联起来，用一句通俗易懂的话讲就是：

当你输入一个网址并按下回车键的时候，首先，应用层协议对该请求包做了格式定义；紧接着传输层协议加上了双方的端口号，确认了双方通信的应用程序；然后网络协议加上了双方的IP地址，确认了双方的网络位置；最后链路层协议加上了双方的MAC地址，确认了双方的物理位置，同时将数据进行分组，形成数据帧，采用广播方式，通过传输介质发送给对方主机。而对于不同网段，该数据包首先会转发给网关路由器，经过多次转发后，最终被发送到目标主机。目标机接收到数据包后，采用对应的协议，对帧数据进行组装，然后再通过一层一层的协议进行解析，最终被应用层的协议解析并交给服务器处理。

## 6.总结

以上内容是对TCP/IP四层模型做了简单的介绍，而实际上每一层模型都有很多协议，每个协议要做的事情也很多，但我们首先得有一个清晰的脉络结构，掌握每一层模型最基本的作用，然后再去丰富细枝末节的东西，也许会更容易理解。

原文链接：https://zhuanlan.zhihu.com/p/273434776

作者：[Hu先生的Linux](https://www.zhihu.com/people/huhu520-10)