本文主题内容
- 理解计算机网络、局域网和广域网
- 理解协议与网络分层
- 对比 OSI 七层模型和 TCP/IP 五层模型
- 理解数据封装与分用
- 理解局域网通信和跨网络通信
- 对比 IP 地址、MAC 地址和端口号
- 掌握网络字节序
- 认识 Socket 地址结构
引言:学习网络编程时,如果只记住
socket、bind等接口,很容易在后续遇到报文、路由和连接问题时失去整体视角。网络通信的主线始终是:数据怎样分层封装、怎样跨越不同网络、怎样找到目标主机中的目标进程。
一、计算机网络
1.1 网络为什么会出现
早期计算机主要以独立模式工作,数据只能在单机内部处理。随着协作需求增加,多台计算机被连接起来,用于交换数据和共享资源,这就形成了计算机网络。
按照覆盖范围,可以粗略分为:
- 局域网 LAN:覆盖家庭、实验室、公司等较小范围
- 广域网 WAN:连接不同地区的网络
- Internet:大量异构网络通过统一协议互联形成的全球网络
局域网和广域网是相对概念。真正重要的不是范围名称,而是不同设备能否按照共同规则完成通信。
1.2 局域网中的设备
常见网络设备包括:
- 网卡:主机接入网络的硬件接口
- 交换机:根据 MAC 地址在局域网内转发以太网帧
- 路由器:根据 IP 地址在不同网络之间转发数据报
主机、交换机和路由器处理的协议层次不同。主机通常实现完整协议栈,交换机主要工作在数据链路层,路由器至少处理到网络层。
二、协议与分层
2.1 什么是协议
协议就是通信双方共同遵守的约定,包括:
- 数据按照什么格式组织
- 每个字段表示什么含义
- 接收方怎样判断一条消息是否完整
- 出现错误后怎样处理
- 数据应该交给哪个上层模块
例如发送三个整数时,如果只把内存中的结构体直接发送,接收方还会面临类型布局、字节序、结构体填充和版本兼容等问题。只有把字段顺序、长度和编码方式约定清楚,双方才能稳定解析。
2.2 为什么要分层
复杂网络不能由一个巨大协议完成全部工作。分层把不同职责拆开,每一层只解决自己的问题,并向上层提供稳定服务。

分层的主要价值是解耦:
- 应用层不需要关心网卡怎样发送电信号
- 传输层不需要知道底层使用以太网还是其他链路
- 网络层不需要理解应用数据的业务含义
- 某一层的实现可以变化,只要对上下层接口保持稳定
网络协议不是相互独立的接口集合,而是一套自上而下封装、自下而上分用的协作体系。
三、OSI 与 TCP/IP 模型
3.1 OSI 七层模型
OSI 模型从上到下包括:
- 应用层
- 表示层
- 会话层
- 传输层
- 网络层
- 数据链路层
- 物理层
它的概念划分完整,但工程中更常使用 TCP/IP 模型。表示层和会话层的许多功能通常由应用程序或库完成。
3.2 TCP/IP 五层模型

| 层次 | 主要职责 | 常见协议或技术 |
|---|---|---|
| 应用层 | 解决具体业务问题 | HTTP、DNS、SSH、自定义协议 |
| 传输层 | 进程到进程的数据传输 | TCP、UDP |
| 网络层 | 跨网络寻址与路由 | IP、ICMP |
| 数据链路层 | 同一链路内传输 | Ethernet、ARP |
| 物理层 | 传输比特流 | 网线、光纤、无线信号 |
从 Linux 编程角度看,应用层通常由用户程序实现,传输层及以下主要由操作系统内核和硬件完成。

四、网络传输的基本过程
4.1 同一主机中的协议栈
应用程序把数据交给 Socket,内核按照传输层、网络层和数据链路层依次处理,最后交给网卡发送。接收端执行相反过程。

每一层都把上层数据看作自己的有效载荷,并添加本层报头:
text
应用数据
TCP/UDP报头 + 应用数据
IP报头 + TCP/UDP报文
以太网首部 + IP数据报 + 以太网尾部
4.2 封装
发送端的数据从上向下传递,每经过一层都添加对应报头,这个过程叫做封装。

不同层的数据通常有不同称呼:
- 传输层:段或数据报
- 网络层:IP 数据报
- 数据链路层:帧
报文可以统一理解为:
text
报文 = 报头 + 有效载荷
4.3 解包与分用
接收端从下向上处理数据:

- 当前层检查并去掉自己的报头
- 根据报头中的协议字段判断应该交给哪个上层协议
- 上层继续执行同样的过程
- 传输层最终根据端口号把数据交给目标进程
学习任何协议时都可以先问两个问题:
- 它怎样确定自己的报头长度,从而完成解包?
- 它根据哪个字段把有效载荷交给上层?
五、局域网与跨网络通信
5.1 局域网通信
同一局域网内,主机最终通过数据链路层发送以太网帧。帧中包含源 MAC 地址和目的 MAC 地址,网卡根据目的 MAC 地址判断是否接收。
交换机会学习端口与 MAC 地址之间的关系,并把帧转发到正确端口。广播帧则会被发送到同一广播域内的多个设备。
5.2 跨网络通信
目标主机不在本地网络时,源主机不会直接把帧发给远端主机,而是把帧交给默认网关。路由器读取目的 IP,查询路由表,再决定下一跳。
每经过一跳:
- 源 IP 和目的 IP 通常保持不变
- 源 MAC 和目的 MAC 会随链路重新封装
- TTL 会递减,防止数据报无限循环

可以把目的 IP 理解为最终目标,把目的 MAC 理解为当前链路上的下一站。
注意:存在 NAT 时,IP 地址也可能被路由设备修改,因此 IP 全程不变只适用于没有地址转换的基本模型。
六、IP 地址、MAC 地址和端口号
6.1 三种标识的职责
| 标识 | 所属层次 | 解决的问题 |
|---|---|---|
| IP 地址 | 网络层 | 目标主机位于哪个网络 |
| MAC 地址 | 数据链路层 | 当前链路把帧交给哪个接口 |
| 端口号 | 传输层 | 数据交给主机中的哪个进程 |
IPv4 地址为 32 位,端口号为 16 位。IP + 端口号 可以标识网络中的一个通信端点。
6.2 端口号与进程
端口号属于网络通信概念,PID 属于操作系统进程管理概念。二者作用不同:
- 一个进程可以使用多个端口
- 通常同一协议、同一地址上的一个端口不能被多个普通 Socket 重复绑定
- 客户端经常由内核分配临时端口
- 服务器端口需要稳定,客户端才能找到它
一条 TCP 连接通常由五元组唯一标识:
text
源IP、源端口、目的IP、目的端口、协议
七、网络字节序
不同 CPU 可能使用不同主机字节序。TCP/IP 规定网络字节序使用大端序,因此多字节整数在发送前需要转换。
常用接口:
c
#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
uint16_t ntohs(uint16_t netshort);
uint32_t htonl(uint32_t hostlong);
uint32_t ntohl(uint32_t netlong);
名称含义:
h:hostn:networks:short,通常用于端口号l:long,通常用于 32 位整数
例:
c
struct sockaddr_in server;
server.sin_port = htons(8080);
八、Socket 地址结构
8.1 通用接口与具体结构
Socket API 同时支持 IPv4、IPv6 和本地通信,因此接口统一接收 struct sockaddr*。实际编写 IPv4 程序时,通常使用 struct sockaddr_in,再进行指针转换。

例:
c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
sockaddr_in 中最重要的内容包括:
sin_family:地址族,IPv4 使用AF_INETsin_port:网络字节序端口号sin_addr:网络字节序 IPv4 地址
8.2 地址转换
推荐使用可重入并支持 IPv4/IPv6 的接口:
c
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
相比之下,inet_ntoa 使用内部静态缓冲区,连续调用可能覆盖上一次结果,不适合保存多个转换结果。
九、总结
计算机网络通过协议让不同主机按照共同规则通信,通过分层把复杂问题拆成多个稳定模块。发送端执行封装,接收端执行解包和分用;IP 地址负责跨网络定位,MAC 地址负责当前链路交付,端口号负责找到目标进程。
TCP/IP 五层模型是后续学习网络协议的主线。应用层定义业务数据,传输层负责进程间传输,网络层负责寻址与路由,数据链路层负责一跳交付。网络字节序和 Socket 地址结构则把这些概念落实到编程接口中。
后续学习每一个协议时,都要同时关注报头格式、解包方法、上层分用字段以及它在完整传输链路中的位置。