学习 Linux 网络编程时,我们很快就会遇到socket()、bind()、connect()、send()等接口。
如果只是记忆函数功能,肯定会有很多疑惑,所以写下这些函数之前,有几个问题值得先弄明白:
- 数据如何跨主机传输:程序交给操作系统的数据,怎样到达另一台计算机?
- 为什么需要多种地址:有了 IP 地址,为什么还需要 MAC 地址和端口号?
- 数据如何找到进程:数据到达目标主机后,操作系统怎样知道该交给哪个程序?
这篇文章从一条消息的传输过程出发,把网络发展、协议、分层、封装、路由和进程通信串起来,为下一篇 Socket 编程建立基础。
目录
- 一、计算机网络背景
- 二、协议:让双方对字节的含义达成一致
- 三、协议分层:把通信问题拆开解决
- 四、封装与分用:数据怎样穿过协议栈
- 五、IP 与 MAC:最终目的地和当前下一跳
一、计算机网络背景
计算机发明出来是为了人类服务的,人类有信息交换的需求,因此不断地探索在计算机层面的数据交换,从军用阿帕网,到 TCP/IP 协议制定后奠定通信基础,再到万维网走向大众。本质就是在把更大范围的机器以更快速的方式连接起来。
1. 独立主机
每一台独立运行的计算机,都可以完成数据的本地处理。
2. 局域网(LAN)
局域网通常覆盖家庭、办公室、校园等较小范围。
局域网中相当于把多台主机连接在同一个物理链路层上,多台设备共享传输介质。共享带来了数据高速传递、延迟低的好处,同时也需要解决多台主机同时发送数据时造成的数据冲突。此时交换机发挥了作用,交换机将每一个端口独立成一个冲突域,通过 MAC 地址表转发实现数据共享。
3. 广域网(WAN)
广域网连接更大范围内的网络,互联网就是最大的广域网。
谈到广域网就不得不聊一下路由器,路由器就像不同队伍的传信员,承担不同局域网数据传递的工作。
了解了路由器的核心功能,这里建立一个认识:通信双方之间可能隔着多段网络,数据需要逐段转发。
“广域网”和“局域网”不过就是一个相对的概念。站在数据的角度,网络通信可以理解为:一个进程通过网络,把数据交给另一个进程。
二、协议:让双方对字节的含义达成一致
正如人类必须通过约定俗成的语言才能进行交流一样,计算机进行数据交换也存在这样一种机制,我们称之为协议。
在数据传递的物理层面,计算机之间的传输媒介是光信号和电信号,通过电平高低和频率来传递 0、1 这样的信息。要想传递各种不同的信息,就需要约定好双方的数据格式。
但是互联网几乎把全球的主机都连接起来了,如果每两台主机间都有自己的数据格式,发送给其他主机就是一串乱码。为此,权威国际组织制定了一套全球共同遵守的“网络协议”,实现了数据在全球范围的流通。
协议主要规定以下三方面内容:
- 数据格式:字节如何组织,各字段如何排列。
- 字段含义:每个字段代表什么信息。
- 通信行为:通信过程中应遵守的规则,例如 TCP 的连接建立、确认和重传。
从 C 语言的角度,可以借助结构体理解协议字段:
struct message_header { uint32_t length; /* 表示有效数据长度 */ uint8_t type; /* 表示数据类型 */ }; /* ...... */但结构体只是程序内的一种表达方式。实际设计网络协议时,还要明确线上每个字节的布局。直接发送整个结构体的内存,可能受到填充、对齐和主机字节序的影响;接收方也可能使用另一种语言。
三、协议分层:把通信问题拆开解决
前面介绍过了,数据通过光电信号在物理层面传递,局域网间交换机完成主机间的数据交换功能,路由器则实现了局域网间的数据交换。
在数据的层层转发中需要考虑:怎样在物理层面传递数据,怎么在同一局域网间互联的主机间正确传递数据,怎么跨局域网在网络上正确传递数据,又要怎么处理传递过程中数据错乱丢失。
协议分层把这些职责拆开。每一层向上提供服务,并使用下一层的能力。这样,应用程序通常不必关心底层使用网线、光纤还是无线链路。
OSI(Open System Interconnection,开放系统互连)七层网络模型称为开放式系统互联参考模型,是一个逻辑上的定义和规范。
TCP/IP 四层模型把数据链路层和物理层相关能力合称为链路层。下表按五层划分梳理各层职责:
| 五层划分 | 主要解决的问题 | 典型协议或技术 |
|---|---|---|
| 应用层 | 数据表达什么业务含义 | HTTP、DNS、自定义应用协议 |
| 传输层 | 怎样在通信端点之间传输数据 | TCP、UDP |
| 网络层 | 怎样寻址并跨网络转发 | IPv4、IPv6 |
| 数据链路层 | 怎样在当前链路上传送帧 | 以太网、Wi-Fi 的链路层机制 |
| 物理层 | 怎样通过介质传递比特 | 电信号、光信号、无线电信号 |
在普通 Linux TCP/UDP 编程中,应用协议通常由用户态程序处理,TCP/IP 等协议主要由内核处理,底层传输由驱动和网卡硬件协作完成。应用通过 Socket 接口使用内核协议栈提供的服务。
还要区分两种关系:
- 同层之间:按协议进行逻辑通信。
- 相邻层之间:通过本机接口交付数据。
发送方的 TCP 与接收方的 TCP 遵守同一个协议,但 TCP 的数据仍然需要经过 IP、链路和实际网络才能到达对方。
四、封装与分用:数据怎样穿过协议栈
1. 封装
假设应用生成了一条消息,并交给 TCP 发送。为了完成各层的职责,协议栈会加入相应的控制信息。下面展示一段应用数据的典型封装关系:
这一过程称为封装。图中的 FCS 是以太网帧末尾的校验字段,因此封装也可能添加尾部信息。
关于封装,有几个关键概念需要明确:
- 报头:添加的协议首部结构体。
- 有效载荷(payload):去掉报头部分后剩下的数据。对某一层来说,由它承载的上层数据就是有效载荷,例如 IP 层承载的有效载荷可以是整个 TCP 报文段。
- 报头内容:通常包含有效载荷长度、报头自身长度、对应协议,以及其他重要信息。
完整报文在不同层中有独特的叫法:
| 所在层 | 报文叫法 |
|---|---|
| 链路层 | 数据帧 |
| 网络层 | 数据报 |
| 传输层 | 数据段 |
2. 分用
数据发送时要层层封装,最后物理传输到目标主机后,上层应用要拿到有效数据,会进行一个反过程。
数据到达接收主机后,协议栈需要逐层检查和解析,然后把有效载荷交给合适的上层处理者。这涉及两个动作:
- 解封装:识别本层控制信息,取出有效载荷。
- 分用:根据协议字段等信息,选择接下来的处理者。
应用拿到字节以后,还需要按应用协议解析业务消息。
学习一种协议时,可以先问自己两个问题:它怎样区分控制信息和有效载荷?它怎样确定有效载荷的下一位处理者?
五、IP 与 MAC:最终目的地和当前下一跳
1. MAC 地址用于当前链路的帧交付
常见以太网 MAC 地址占 48 位,即 6 字节,例如:08:00:27:03:fb:19。
MAC 地址在网卡出厂时就确定了,不能修改。MAC 地址通常是唯一的(虚拟机中的 MAC 地址不是真实的 MAC 地址,可能会冲突;也有些网卡支持用户配置 MAC 地址)。
以太网帧中包含源 MAC 和目的 MAC。发送方需要确定:当前这一段链路上,帧应该交给谁?
如果目标在本地链路上,目的 MAC 通常是目标接口的 MAC;如果需要经网关转发,目的 MAC 通常是网关在本地链路上的接口 MAC。IPv4 以太网中,主机可以通过 ARP 获取下一跳 IP 对应的 MAC,并缓存结果。
以太网与交换机的几个要点:
- 以太网中通过 MAC 地址判定数据是否发送给自己。
- 以太网中任何时刻只允许一台主机发送数据。
- 多台主机同时发送数据,会导致数据错乱,称为数据碰撞。
- 数据碰撞本质是因为之前局域网多台主机共用一个总线,数据串行传递会发生干扰。
- 交换机通过自学习缓存建立 MAC 表,只将数据转发给目的端口,相当于给数据交换主机间开通了一个专属会话室,提高了效率并避免数据碰撞。
2. IP 地址用于网络层寻址
IPv4 地址占 32 位,即 4 字节,通常写成点分十进制形式,例如192.168.1.10。每一部分对应一个字节,取值范围为 0~255。
跨网段的主机的数据传输,数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。
假设一种场景:
- 主机 A:
192.168.1.10/24 - 主机 B:
192.168.1.20/24 - 主机 C:
192.168.2.20/24
/24表示前 24 位为网络前缀,对应掩码255.255.255.0。在普通配置下,A 到 B 可以在本地链路直接交付,正如上面 MAC 介绍;A 到 C 则需要经过网关,如下图。
假设 A 的路由表指向这个网关,且不涉及 NAT、隧道等处理:
| 传输位置 | 源 IP → 目的 IP | 源 MAC → 目的 MAC |
|---|---|---|
| A 到路由器 | A 的 IP → B 的 IP | A 的 MAC → R1 的 MAC |
| 路由器到 B | A 的 IP → B 的 IP | R2 的 MAC → B 的 MAC |
3. IP 在网络宏观传输过程中的作用
结合之前涉及到的封装和解封,当我们向远在天边的朋友发送消息时,数据在主机 A 经过层层封装后以数据帧的形式从网卡发送向路由器,路由器解包拿到网络层封装报头中包含的源 IP、目的 IP,通过掩码比对后,封装并发往下一站,通过层层跳转,将数据传递到目标主机 B。
综上,源 IP 记录你最初的出发地,目的 IP 指向最终目的地,源 MAC 记录数据途径的上一站,目的 MAC 指向当前链路上的下一跳。
- IP 地址在整个路由过程中,一直不变(目前阶段这样理解)。
- MAC 地址一直在变,指向下一个“驿站”。
- 目的 IP是路径选择的重要依据,MAC 地址是局域网转发的重要依据。
4. 一个生动的类比
为了更好理解 IP 和 MAC,我们可以举一个生动的例子。《西游记》中唐僧师徒从长安出发前往西天取经,唐僧途经车迟国时,面对国王会说,贫僧从东土大唐而来,要前往西天求取真经,国王前往西天,我们下一站要去哪?国王会给唐僧通过文牒,并告诉他们下一站是通天河。
在这个过程中,唐僧向国王这个路由器展示了自己的源 IP(长安)、目的 IP(西天),国王的通关文牒就是修饰唐僧的源 MAC,通天河就是他们眼下的目的 MAC。
浅显理解就是,目的 IP 是长远目标,而目的 MAC 是阶段性目标,当我们达成一个阶段性目标后,会制定下一个,不断接近长远目标。
有了这些基本认识,我们后面就能更方便地理解端口、UDP、TCP 协议,以及 Socket 相关函数的关联了。