一、网络通信的底层逻辑:协议栈与封装
数据如何传输?通过分层协作实现。就像快递送货,每层只负责特定环节,不越界。
1. Linux 网络协议栈分层
- 应用层:给数据定格式,如 HTTP、SSH、FTP。
- 传输层:给数据标进程,用端口号区分程序(如 80 端口是浏览器),核心是 TCP 和 UDP。
- 网络层:给数据标主机,用 IP 地址定位目标电脑。
- 数据链路层:给数据标节点,用 MAC 地址在局域网内传输。
- 物理层:纯硬件传输,如网线传电信号、光纤传光信号。
在 Socket 网络编程(IPv4 场景)中,sockaddr_in结构体的 sin_addr字段以 32 位二进制形式存储 IP 地址,但日常习惯用点分十进制字符串表示,因此需要借助地址转换函数实现互转。
地址转换函数
需引入头文件 <arpa/inet.h>。
1. 字符串转 in_addr
inet_aton(const char *strptr, struct in_addr *addrptr):将点分十进制字符串转为 in_addr 结构体,成功返回 1,失败返回 0。inet_addr(const char *strptr):直接返回转换后的 32 位整数形式 IP,无法区分转换失败与 0.0.0.0。inet_pton(int family, const char *strptr, void *addrptr):更通用的函数(支持 IPv4/IPv6),family 传 AF_INET 表示 IPv4。
2. in_addr 转字符串
inet_ntoa(struct in_addr inaddr):将 in_addr 结构体转为点分十进制字符串,但返回的是静态缓冲区,多线程场景下可能被覆盖。inet_ntop(int family, const void *addrptr, char *strptr, size_t len):更安全的通用函数,需手动传入缓冲区存储结果,避免线程安全问题。

2. 数据封装与解封装
发送方流程:应用层生成原始数据 -> 传输层加头(源/目的端口)变成报文段 -> 网络层加头(源/目的 IP)变成数据报 -> 数据链路层加头和尾(MAC 地址)变成以太网帧 -> 物理层变成电信号发送。 接收方反向操作:拆帧 -> 拆数据报 -> 拆报文段 -> 拿到原始数据。
以下展示 C++ 网络编程中封装 IP 与端口处理的 InetAddr 类代码示例:
#include <arpa/inet.h>
#include <string>
#include <cstdint>
#include <cstring>
class InetAddr {
public:
// 构造函数 1:从 sockaddr_in(网络字节序)初始化
InetAddr(const struct sockaddr_in& addr) : _addr(addr) {
// 网络字节序端口 -> 主机字节序
_port = ntohs(addr.sin_port);
// 网络字节序 IP -> 点分十进制字符串
_ip = inet_ntoa(addr.sin_addr);
}
// 构造函数 2:从 IP 字符串(主机序)和端口(主机序)初始化
InetAddr(const std::string& ip, uint16_t port) : _ip(ip), _port(port) {
memset(&_addr, 0, sizeof(_addr));
_addr.sin_family = AF_INET;
// 主机字节序端口 -> 网络字节序
_addr.sin_port = htons(port);
// 点分十进制 IP 字符串 -> 网络字节序
inet_pton(AF_INET, ip.c_str(), &_addr.sin_addr);
}
// 获取主机字节序的端口
uint16_t Port() const { return _port; }
// 获取点分十进制的 IP 字符串
std::string Ip() const { return _ip; }
// 获取 sockaddr_in 结构体(网络字节序)
const struct sockaddr_in& GetSockAddr() const { return _addr; }
private:
struct sockaddr_in _addr; // 网络字节序的地址结构体
std::string _ip; // 主机序:点分十进制 IP 字符串
uint16_t _port; // 主机序:端口号
};
二、网络定位核心:IP 地址与端口
IP 地址类似小区地址,端口号类似门牌号。只有同时知道这两个,数据才能准确送到目标进程。
1. IP 地址
- 作用:定位主机(电脑、服务器、路由器)。
- 格式:IPv4 是 32 位二进制(如 192.168.1.1);IPv6 是 128 位。
2. 端口号
- 作用:定位主机上的进程,是 16 位整数(0-65535)。
- 分类:
- 知名端口(0-1023):系统预留,如 80=HTTP、443=HTTPS、22=SSH。
- 注册端口(1024-49151):应用程序注册使用,如 3306=MySQL。
- 动态端口(49152-65535):客户端临时用,通信结束释放。
3. 关键结论
"IP+ 端口"= 唯一通信端点(如 192.168.1.1:80)。在网络编程中,inet_ntoa 存在静态缓冲区覆盖问题,建议使用 inet_ntop 替代。

三、Linux 网络管理必备工具
| 工具 | 核心功能 | 常用命令示例 | 说明 |
|---|---|---|---|
| ip addr | 查看/配置 IP 地址 | ip addr add 192.168.1.100/24 dev eth0 | 给网卡加 IP,/24 是子网掩码 |
| ip route | 查看/配置路由表 | ip route add default via 192.168.1.1 | 设置默认网关 |
| ping | 测试主机连通性 | ping -c 4 www.baidu.com | 发测试包看延迟和通断 |
| ss/netstat | 查看网络连接状态 | ss -tulnp | 查哪个进程占用了端口 |
| tcpdump/Wireshark | 网络抓包分析 | tcpdump -i eth0 port 80 | 捕获指定网卡端口的数据 |
Windows 平台需引入 WinSock2.h 头文件,并通过 #pragma comment(lib, "ws2_32.lib") 链接库,且需调用 WSAStartup 初始化套接字库,这是 Windows 平台独有的步骤,Linux 无需此操作。
跨平台 UDP 通信示例显示,客户端填写服务器 IP 和端口创建套接字发送数据,服务器日志可显示客户端消息,说明通信已建立。

四、TCP 协议核心:可靠传输机制
TCP 保证数据不丢、不错、不乱,核心机制如下。
1. TCP 报文结构
TCP 报文由固定头部(20 字节)+ 选项 + 数据组成。核心字段包括序号/确认号(保证可靠有序)、窗口大小(流量控制)、标志位(SYN、ACK、FIN、RST)。
2. 连接管理:三次握手
为什么是三次?两次无法确认双方都能收发,四次多余。流程如下:
- 客户端 → 服务器:SYN(序号 x)。
- 服务器 → 客户端:SYN+ACK(序号 y,确认号 x+1)。
- 客户端 → 服务器:ACK(确认号 y+1)。 最终双方状态:ESTABLISHED。
初始化 socket 地址时,需清空结构体,指定协议族 AF_INET,绑定所有本地 IP(INADDR_ANY),并将端口转换为主机字节序。

3. 连接管理:四次挥手
TCP 是全双工通信,关闭连接需分两步:先关发送通道,再关接收通道。
- 主动方 → 被动方:FIN。
- 被动方 → 主动方:ACK。
- 被动方 → 主动方:FIN。
- 主动方 → 被动方:ACK。 注意:主动方最后进入 TIME_WAIT 状态,等待 60 秒防止最后一个 ACK 丢失。
4. 可靠性保障:序号、确认与重传
- 序号:给每个字节编号,乱序也能排好。
- 确认号:告知发送方已收到数据及下一个期望序号。
- 重传:超时未收到确认则重发,或收到 3 个重复确认触发快速重传。
5. 流量控制:滑动窗口
接收方告诉发送方还能装多少数据(窗口大小),发送方只发窗口内的数据,避免接收方缓冲区溢出。
6. 拥塞控制:适应网络状态
考虑网络链路能力,核心是拥塞窗口(cwnd)。
- 慢开始:cwnd 指数增长直到阈值。
- 拥塞避免:达到阈值后线性增长。
- 快速恢复:收到 3 个重复 ACK 时减半阈值,直接进入拥塞避免。
五、TCP vs UDP:协议选择逻辑
- TCP(快递):慢但可靠,适合转账、文件传输。
- UDP(对讲机):快但不保证送达,适合直播、游戏。
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠 | 不可靠 |
| 有序性 | 保证有序 | 不保证有序 |
| 速度 | 慢 | 快 |
| 适用场景 | HTTP、MySQL | 直播、DNS |

六、Linux 内核 TCP/IP 与网络编程
写网络程序本质是调用内核接口(socket),让内核处理 TCP/IP 细节。
1. 内核协议栈流转路径
应用程序 → socket 层(应用与内核接口)→ inet 层(处理 TCP/UDP)→ IP 层(处理 IP 地址)→ 数据链路层(处理 MAC 地址)→ 网卡(发送数据)。 理解这一路径有助于排查网络问题,例如通过抓包工具分析各层数据是否正常,从而定位是应用层逻辑错误还是内核网络栈异常。

