
# 第一周深度学习复盘从“手动管理”转向“标准工具”及网络底层实战## 整体学习路线回顾本阶段从 C 标准模板库STL的内存管理底层出发逐步延伸至网络协议栈的封包/解包逻辑最后打通了操作系统层面的 Socket API 与多路复用Epoll机制。全程围绕“高性能”、“内存安全”与“零拷贝”展开。---## 模块一C 内存管理与序列式容器 (std::vector)### 核心概念内存搬运的代价* **知识点**vector 在内存中是连续存储的。当容量不足时会触发重新分配Reallocation导致全量对象的析构与重新拷贝且会使旧迭代器失效。* **reserve() 与 malloc 的区别**malloc 只是分配原始字节reserve() 是预留对象空间但不调用构造函数直到 push_back 才真正创建对象。### 疑问与纠错记录* **❌ 用户的疑问**capacity 需要预先分配吗我刚刚的每次 push 应该都涉及到了扩容。* **✅ 逻辑纠正**强烈建议预先分配。默认 capacity 为 0不预先分配会导致极其频繁的按比例扩容搬家。通过 v1.reserve(n) 可以一次性锁定连续内存彻底避免后续插入时的内存分配开销。---## 模块二现代 C 字符串处理 (std::string_view)### 核心概念零拷贝解析技术* **知识点**std::string_view 内部只维护一个指针指向真实内存和一个长度size_t。它本身不拥有数据在进行 substr 切片时没有任何内存分配和数据拷贝极大地提升了网络协议如 IP、HTTP的解析速度。### 疑问与纠错记录* **❌ 用户的疑问 1**string_view 继承了 string 吗底层是怎么操作的* **✅ 逻辑纠正**完全没有继承关系。它们是平级关系房东与租客。string 拥有内存空间而 string_view 仅仅是一个“观察窗口”底层仅有 const char* 和 size_t 两个变量共 16 字节。* **❌ 用户的疑问 2**解析 IP 段时为什么 find(. || /) 会陷入死循环或者报错* **✅ 逻辑纠正**在 C 中 . || / 是布尔逻辑运算结果为 1。必须使用 find_first_of(./) 才能匹配集合中的任意字符。* **❌ 用户的疑问 3**如何用循环切分 IP 地址* **✅ 逻辑纠正**需要维护一个动态更新的 start 指针利用 substr(start, pos - start) 截取片段并将 start 更新为 pos 1直到 pos npos。---## 模块三关联式容器与算法 (std::set, std::map, Lambda)### 核心概念查找利器与红黑树* **知识点**std::map / std::set 底层采用红黑树自平衡二叉搜索树查找复杂度为 $O(\log n)$unordered_map 采用哈希表理论复杂度为 $O(1)$。网络设备中的路由表、MAC 地址表高度依赖此类容器。### 疑问与纠错记录* **❌ 用户的疑问 1**为什么尝试对 std::setstd::string 调用 std::sort 会报编译错误* **✅ 逻辑纠正**std::set 插入即有序红黑树的天性。它的迭代器是只读的const不支持 std::sort 所需的随机访问和“暴力元素交换”。如果要自定义排序需要在定义容器时传入比较器如 std::greater。* **❌ 用户的疑问 2**为什么 IP 地址排序变成了 1.10 在 1.2 前面* **✅ 逻辑纠正**因为默认使用的是字符串字典序Lexicographical Order按 ASCII 码逐字比较。网络编程中必须先将 IP 段转为数字如 32 位无符号整数再进行比较。---## 模块四移动语义 (std::move)### 核心概念所有权的转移* **知识点**在嵌入式等对性能极度敏感的场景大对象的深拷贝是不可接受的。移动语义通过“转移指针所有权”并清空原指针置为 nullptr实现了资源的零开销交接。### 疑问与纠错记录* **❌ 用户的疑问**std::move 到底动了什么* **✅ 逻辑纠正**std::move 本身**什么都没动**它只是一个将左值强转为右值的标记。真正的“偷资源”动作发生在你自定义的**移动构造函数** T(T other) 中。---## 模块五计算机网络底层实战 (Wireshark 抓包)### 核心概念OSI 模型封装与参数映射* **知识点**数据包从应用层到物理层层层封装。MAC 地址解决局域网下一跳Switch/ARPIP 地址解决跨网寻址Router端口号解决进程区分与 NAT 映射。### 疑问与纠错记录* **❌ 用户的疑问 1**抓包里的源 IP 是 192.168.x.x源端口是随机的怎么看我的公网 IP* **✅ 逻辑纠正**在终端主机如笔记本上抓包是看不到公网 IP 的。内网 IP 必须经过网关路由器的 NAT网络地址转换才能替换为公网 IP抓包位置决定了视野。* **❌ 用户的疑问 2**手动计算 IPv4 Checksum 时结果算成了 0xE3。对于“进位加回低 16 位”的位运算不清楚。* **✅ 逻辑纠正**IPv4 校验和是 16 位的反码累加。1. 必须考虑网络字节序大端序。2. 当使用 uint32_t 累加产生超出 16 位的溢出时位运算写法为sum (sum 0xFFFF) (sum 16)。3. 这个动作可能产生新的进位因此必须套在 while(sum 16) 循环中最后再进行按位取反 ~sum。---## 模块六TCP 状态机与性能博弈### 核心概念连接的生死轮回* **知识点**TCP 标志位控制着状态转移。三次握手为了同步 ISN 并确认双向收发能力四次挥手为了全双工的安全关闭。### 疑问与纠错记录* **❌ 用户的疑问 1**为什么会有 PSH 和 ACK 同时出现它意味着什么* **✅ 逻辑纠正**PSH 要求接收端操作系统不等待缓冲区填满立刻把数据上报给应用层ACK 则是对之前收到数据的常规确认。* **❌ 用户的疑问 2**PSH 是不是减轻了内存压力但增大了 CPU 压力* **✅ 逻辑纠正**思考极其深刻是的PSH 避免了发送方的内存积压但极其频繁的小包会引发海量的网卡中断和内核态/用户态上下文切换极大拉高系统 CPU 开销。解决手段通常是 Nagle 算法牺牲延迟换吞吐或网卡硬件卸载TSO。* **❌ 用户的疑问 3**为什么不能两次握手为什么挥手要有 TIME_WAIT* **✅ 逻辑纠正*** 两次握手防不住“迷路后迟到的旧 SYN 包”会导致服务器白白分配资源死等。* TIME_WAIT2MSL的作用是兜底最后一个 ACK并确保当前连接在网络中残留的所有包消亡防止污染复用该端口的新连接。---## 模块七Socket 编程逻辑与 Epoll 底层### 核心概念从单线到并发* **知识点**理解 Socket API 的背后内核动作。socket 申请 FDbind 占领端口listen 初始化连接队列accept 摘取已建立的连接。### 疑问与纠错记录* **❌ 用户的疑问 1**为什么 Server 需要 listen 和 client_fd 两个而 Client 只需要一个* **✅ 逻辑纠正**Server 面临一对多的场景。listen_fd 像酒店大门只负责拦截 SYNconn_fd (client_fd) 像专属房间负责数据传输。职责分离才能实现并发。* **❌ 用户的疑问 2**既然 ET边缘触发能减少 epoll_wait 调用为什么还有人如 Redis/早期框架用 LT水平触发* **✅ 逻辑纠正**LT 模式开发更简单、容错率高没读完下次还会提醒并且兼容阻塞 IO。ET 模式性能极高但强制要求非阻塞 IO 且必须配合循环读取直至 EAGAIN 报错稍有不慎就会死锁或丢数据。没有绝对的好坏只有业务场景的权衡。