学习向复刻操作系统传输层:从零实现TCP/UDP核心机制

一、项目背景与核心定位

1.1 项目背景

在计算机网络学习中,传输层(TCP/UDP)是连接应用层与网络层的关键桥梁。然而,大多数开发者仅停留在API调用层面,对底层实现机制知之甚少。本项目通过从零手写传输层核心代码,深入理解操作系统内核如何为上层应用屏蔽复杂网络细节。

1.2 核心目标

本项目仅用于底层网络知识学习理解,不追求工业级高并发、高稳定、全量RFC扩展特性;核心目标两点:

  1. 手写TCP、UDP完整基础实现,吃透UDP无连接不可靠、TCP面向连接可靠传输底层全部原生代码逻辑;
  2. 完整看懂操作系统内核为普通上层业务程序屏蔽了海量复杂底层工作,补齐传输层底层知识短板。

1.3 整体分层架构

本项目采用自顶向下的纯网络分层设计,与上层业务应用协议完全解耦:

层级 模块名称 功能定位 实现说明
应用层 极简Socket抽象层 复刻操作系统用户态套接字接口 向上提供统一字节读写API,屏蔽底层TCP/UDP差异
传输层 自研传输层(UDP + TCP核心骨架) 本次核心开发模块 手写TCP可靠传输、UDP不可靠传输全部原生逻辑
网络层 IP层承载 复用Linux SOCK_RAW 不重复自研IP/网卡,聚焦传输层学习

架构设计原则

  • 自上而下:上层调用下层服务,下层为上层提供数据收发能力
  • 职责隔离:每层仅关注本层核心逻辑,层间通过标准接口交互
  • 学习聚焦:IP层复用现有实现,避免分散精力到网络层/链路层细节## 二、前置理论:UDP与TCP底层本质差异## 三、公共支撑底座实现逻辑## 四、UDP子系统完整底层实现逻辑## 四、TCP子系统完整底层实现逻辑
    每一条TCP连接独立分配tcp_sock控制块,承载连接全部状态、缓冲区、序列号、窗口、定时器,是TCP可靠传输的核心载体。

1. tcp_sock 核心结构体字段

// tcp_sock 核心结构体(简化版)
typedef struct tcp_sock {
    // 连接基础
    quintuple_t q;          // 五元组
    tcp_state_t state;      // TCP状态
    
    // 序列号体系
    uint32_t snd_nxt;       // 下一个待发送seq
    uint32_t rcv_nxt;       // 下一个期望接收ack
    uint32_t snd_una;       // 已确认最大序号
    
    // 窗口控制
    uint16_t rcv_wnd;       // 本地接收剩余窗口
    uint16_t snd_wnd;       // 对端通告发送窗口
    
    // 缓冲区
    ring_buffer_t* snd_buf; // 发送环形缓冲区
    ring_buffer_t* rcv_buf; // 接收环形缓冲区
    
    // 重传管理
    list_t* retrans_queue;  // 重传队列
    uint32_t rto;           // 重传超时时间
    
    // 定时器
    timer_handle_t retrans_timer;
    timer_handle_t timewait_timer;
    timer_handle_t keepalive_timer;
    
    // 上层绑定
    socket_t* socket;       // 关联Socket句柄
} tcp_sock_t;
  1. 连接基础:五元组、当前TCP状态枚举;
  2. 序列号体系:下一个待发送seq、下一个期望接收ack、已确认最大序号;
  3. 窗口控制:本地接收剩余窗口、对端通告发送窗口;
  4. 缓冲区:发送环形缓冲区(待发送/未ACK数据)、接收环形缓冲区(有序字节流);
  5. 重传管理:重传队列(存储未确认报文段)、RTO超时基准时间;
  6. 定时器绑定:重传、TIME_WAIT、保活定时器句柄;
  7. 上层绑定:关联对应Socket句柄。

2. TCP完整状态机编码实现

// TCP状态机处理函数(简化示例)
void tcp_state_machine(tcp_sock_t* sk, tcp_segment_t* seg) {
    switch (sk->state) {
        case LISTEN:
            if (seg->flags & SYN) {
                sk->state = SYN_RCVD;
                send_syn_ack(sk);
            }
            break;
            
        case SYN_SENT:
            if (seg->flags & SYN && seg->flags & ACK) {
                sk->state = ESTABLISHED;
                send_ack(sk);
            }
            break;
            
        case ESTABLISHED:
            if (seg->flags & FIN) {
                sk->state = CLOSE_WAIT;
                send_ack(sk);
            }
            break;
            
        case FIN_WAIT_1:
            if (seg->flags & ACK) {
                sk->state = FIN_WAIT_2;
            }
            break;
            
        // ... 其他状态处理
    }
}

枚举全部标准TCP状态:LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED、FIN_WAIT_1、FIN_WAIT_2、CLOSING、TIME_WAIT、CLOSED

核心状态流转分支逻辑
  1. 客户端建连:connect发送SYN → SYN_SENT;收到SYN+ACK并回复ACK → ESTABLISHED;
  2. 服务端监听:listen保持LISTEN;收到SYN回复SYN+ACK → SYN_RCVD;收到客户端ACK → ESTABLISHED;
  3. 主动关闭连接:发送FIN进入FIN_WAIT_1;收到对方ACK → FIN_WAIT_2;收到对方FIN并回复ACK → TIME_WAIT;定时器2MSL到期销毁连接;
  4. 被动关闭连接:收到FIN立即回复ACK;上层调用close发送FIN进入CLOSING;收到对方ACK切换为CLOSED。
学习意义

上层业务仅调用connect/listen/close简单接口,内核内部需要处理几十种异常报文场景的状态合法性判断与流转,大量分支逻辑全部被底层屏蔽。

3. 三大可靠传输核心模块底层编码逻辑

(1)序列号与ACK确认机制
  1. 每一段发送数据分配递增seq序号,唯一标识每一字节数据流;
  2. 接收端回复ACK字段=期望接收的下一字节序号,告知发送方该序号前所有数据已完整接收;
  3. 发送方收到匹配ACK,从重传队列清除对应报文,释放发送缓冲区空间。
(2)超时重传机制
  1. 报文发送时存入重传队列,同步启动重传定时器;
  2. 定时器到期未收到对应ACK,自动重发队列内报文,重置计时;
  3. 收到有效ACK则停止定时器、清理重传条目,解决网络丢包问题。
(3)滑动窗口流量控制
  1. 接收端每次回复ACK携带window值,代表本地缓冲区剩余可存储字节;
  2. 发送方累计发送数据总量严格小于对端通告窗口,避免接收缓冲区溢出;
  3. 接收缓冲区释放空间后,后续ACK自动更新更大窗口,放开发送速率限制。

4. 收发环形缓冲区底层逻辑

发送缓冲区

上层send写入数据,缓冲区满则阻塞线程;依据对端窗口拆分TCP分段、封装头部下发IP;未收到ACK的数据永久保留在重传队列,直至确认。

接收缓冲区

有序报文直接存入缓冲区,唤醒上层recv;乱序报文存入独立临时缓存,等待缺失序号补齐后,合并为连续字节流再交付上层。

5. 配套定时器业务逻辑

  1. 重传定时器:处理报文丢失场景;
  2. TIME_WAIT定时器:主动关闭后等待2MSL,防止残留报文干扰新连接;
  3. TCP保活定时器:长时间无数据交互,发送探测报文,清理僵死闲置连接。

TCP学习阶段简化取舍

  1. 不实现SACK、快速重传、BBR/CUBIC完整拥塞控制,仅简易Reno窗口调整;
  2. 不支持PMTU动态探测、MSS自适应调整;
  3. 无SYN洪水防护、并发连接限流;
  4. 单线程处理报文,不做多核分段锁、多队列并发优化。

五、极简Socket抽象层底层实现逻辑(复刻操作系统用户态接口)

设计目标

完整复刻操作系统Socket分层隔离设计思想,屏蔽底层TCP/UDP全部复杂逻辑,向上层提供统一无差异的字节读写接口,完全独立于任何上层业务协议。

仅实现阻塞模式核心标准API

  1. socket(int type):创建套接字,区分SOCK_STREAM(TCP) / SOCK_DGRAM(UDP),分配独立缓冲区、句柄;
  2. bind(uint16_t port):绑定本地端口,写入五元组哈希表;
  3. listen(int backlog):仅TCP使用,切换套接字至监听状态;
  4. accept():TCP阻塞等待客户端握手完成,返回全新连接套接字句柄;
  5. connect(ip, port):TCP客户端主动发起三次握手,阻塞直至建连成功或失败;
  6. send(buf, len):TCP流式发送,写入TCP发送环形缓冲区;
  7. recv(buf, len):阻塞读取有序字节流;
  8. sendto/recvfrom:UDP专用无连接收发接口;
  9. close():触发TCP四次挥手流程 / 释放UDP端口,注销全部定时器、释放缓冲区内存。

Socket分层隔离的核心价值

  1. 上层业务代码完全不用区分底层TCP、UDP实现细节;
  2. 传输层修改重传、窗口、状态机底层逻辑,Socket上层调用接口无需任何改动;
  3. 清晰区分边界:Socket是用户态与传输层内核的标准隔离层,上层仅负责业务数据编解码,底层传输可靠性全部由传输层承载。

六、端到端完整TCP数据流转链路

  1. 上层通用业务调用socket.send写入一段二进制字节;
  2. Socket层将数据拷贝至TCP发送环形缓冲区,缓冲区满则阻塞线程;
  3. TCP层读取缓冲区数据,依据对端通告窗口拆分TCP分段;
  4. 填充TCP头部seq、ack、window、标志位,计算完整校验和;
  5. 报文加入重传队列,启动重传定时器;
  6. 向下调用IP原始套接字接口,封装IP头部对外发送;
  7. 对端网卡接收报文,经IP层上交自研传输层;
  8. TCP模块校验和校验,解析seq、ack、window字段;
  9. 有序数据存入接收缓冲区,回复携带窗口信息的ACK报文;
  10. 本地收到对端ACK,从重传队列清理已确认分段,释放发送缓冲区;
  11. 对端调用socket.recv,读取连续有序字节流,交付上层业务。

整条链路中,五元组匹配、序列号管理、重传队列、窗口控制、状态切换、定时器调度、伪首部校验全部是自研传输层代码实现;真实操作系统环境下,以上所有逻辑全部由内核自动执行,上层业务无感知。

七、复刻传输层核心难点与对应学习意义

难点1:TCP多场景状态机跳转

数十种报文组合对应不同状态变更,一处判断遗漏直接握手失败、连接卡死。
学习意义:直观理解操作系统内核需要覆盖海量网络异常边界,上层业务无需处理各类畸形报文分支。

难点2:seq、ack、window数值联动计算

序列号、确认号、通告窗口三者强耦合,数值计算偏差会出现丢数据、重复接收、发送永久阻塞。
学习意义:内核自动维护全套序列号与窗口数值运算,上层业务完全不用做数值运算。

难点3:定时器全生命周期管理

重传、TIME_WAIT、保活定时器绑定独立连接,连接销毁必须同步注销定时任务,极易出现内存堆积。
学习意义:操作系统内核自动管理连接定时器生命周期,长时间运行不会产生资源泄漏。

难点4:多上下文并发资源竞争

五元组哈希表、tcp_sock连接块会被收包回调、定时器、上层业务线程同时访问,不加锁直接内存越界崩溃。
学习意义:内核内置细粒度分段锁、多CPU队列隔离并发冲突,上层业务代码无任何并发保护负担。

八、整体总结

  1. 本复刻项目聚焦底层传输层原生实现,仅研究TCP/UDP、Socket抽象、五元组、定时器、滑动窗口、重传、状态机等网络基础设施,和上层自定义应用业务协议无关联;
  2. 通过手写代码直观区分UDP无连接、无确认、无流控带来的天然不可靠特性,以及TCP一整套完备可靠传输底层实现;
  3. 复刻极简Socket层,吃透操作系统分层解耦的核心设计思路,清晰划分用户态上层业务与内核传输层的职责边界;
  4. 完整梳理操作系统内核为上层业务屏蔽的全部复杂底层工作,补齐TCP/IP传输层底层知识盲区,后续排查网络丢包、连接阻塞、吞吐异常等问题能够直达底层根源。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐