当前位置: 首页 > news >正文

RAW API 的 TCP 总结2

主要内容参照17. 使用RAW API接口编程 — [野火]LwIP应用开发实战指南—基于野火STM32 文档,整理出来自用。

RAW API 的 TCP 编程本质是对TCP 控制块(PCB) 的全生命周期操作,涵盖控制块的创建、绑定、监听、连接管理、数据收发及异常处理等核心流程。

1. 新建 TCP 控制块:tcp_new()

  • 核心功能:分配并初始化一个新的 TCP 控制块,为后续 TCP 连接奠定基础。
  • 实现逻辑
    1. 调用tcp_alloc(TCP_PRIO_NORMAL)分配 TCP 控制块结构,优先级为 “正常”;
    2. 若空间不足,tcp_alloc()会释放低优先级 / 非关键状态的控制块(如TIME_WAITCLOSING状态),优先满足新控制块分配;
    3. 分配成功后,内核自动初始化控制块的各个字段(如端口、IP、状态等)。
  • 源码核心
    struct tcp_pcb *tcp_new(void) { return tcp_alloc(TCP_PRIO_NORMAL); }
    

2. 绑定控制块:tcp_bind()

  • 核心功能:将本地 IP 地址、端口号与 TCP 控制块绑定(仅服务器端常用),确保端口不冲突。
  • 实现逻辑
    1. 处理默认参数:若 IP 为NULL,自动绑定IP4_ADDR_ANY(任意本地 IP);若端口为 0,调用tcp_new_port()分配随机可用端口;
    2. 端口冲突检测:遍历 LwIP 的 4 条 TCP 控制块链表(tcp_pcb_lists),检查目标 IP + 端口是否已被占用,若占用返回ERR_USE
    3. 绑定与挂载:设置控制块的local_iplocal_port,并将其插入tcp_bound_pcbs(绑定状态链表)。
  • 关键返回值ERR_OK(成功)、ERR_USE(端口冲突)、ERR_BUF(端口分配失败)。

3. 监听连接:tcp_listen()

  • 核心功能:将绑定后的控制块转为 “监听状态”,等待客户端连接(仅服务器端),并优化内存占用。
  • 核心设计:LwIP 为监听状态单独设计tcp_pcb_listen(精简版控制块),仅包含监听所需字段(如本地 IP / 端口、回调参数),比完整控制块更节省内存(适配嵌入式场景)。
  • 实现逻辑
    1. 状态检查:若控制块已为LISTEN状态,直接返回并提示ERR_ALREADY
    2. 分配精简控制块:调用memp_malloc(MEMP_TCP_PCB_LISTEN)分配tcp_pcb_listen,拷贝完整控制块的关键字段(如local_portprio);
    3. 状态切换与资源清理:将完整控制块从tcp_bound_pcbs移除并释放,设置精简控制块状态为LISTEN,插入tcp_listen_pcbs(监听状态链表);
    4. 客户端连接处理:当收到客户端SYN报文时,内核遍历tcp_listen_pcbs找到匹配控制块,新建完整控制块(拷贝精简块字段),填写客户端 IP / 端口,挂载到tcp_active_pcbs(活跃连接链表),精简块保留以继续监听其他连接。

4. 处理客户端连接:tcp_accept()

  • 核心功能:为监听状态的控制块注册 “连接回调函数”,当客户端连接请求被接受时,内核自动触发该回调。
  • 实现逻辑
    1. 仅对LISTEN状态的控制块生效,将tcp_accept_fn类型的回调函数赋值给tcp_pcb_listenaccept字段;
    2. 回调函数参数:newpcb(新连接对应的完整控制块,需用户后续处理)、arg(自定义参数)、err(错误码)。
  • 源码核心
    typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);
    void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept) {if (pcb && pcb->state == LISTEN) ((struct tcp_pcb_listen *)pcb)->accept = accept;
    }
    

5. 建立客户端连接:tcp_connect()

  • 核心功能:客户端主动向服务器发起 TCP 连接(三次握手的起点),将控制块从 “绑定状态” 转为 “活跃状态”。
  • 实现逻辑
    1. 配置连接参数:设置服务器 IP(remote_ip)和端口(remote_port),通过ip_route()确定发送报文的网卡(netif);
    2. 本地参数补全:若未绑定本地 IP / 端口,自动绑定网卡的本地 IP 和随机端口;
    3. 初始化窗口与序号:设置发送序号(iss)、接收窗口(rcv_wnd)、拥塞窗口(cwnd=1)等 TCP 核心参数;
    4. 发起连接:调用tcp_enqueue_flags(pcb, TCP_SYN)构造SYN报文,控制块状态改为SYN_SENT,从tcp_bound_pcbs移除并插入tcp_active_pcbs,调用tcp_output()发送SYN报文;
    5. 连接回调:需传入tcp_connected_fn回调函数,连接建立(三次握手完成)后内核触发该回调。

6. 终止连接:tcp_close()(依赖tcp_close_shutdown()

  • 核心功能:主动终止 TCP 连接,根据控制块当前状态执行不同清理逻辑(需结合 TCP 状态转移图理解)。
  • 状态化处理逻辑
    控制块状态处理方式
    CLOSEDtcp_bound_pcbs移除,释放控制块内存
    LISTENtcp_listen_pcbs移除,调用tcp_free_listen()释放精简控制块
    SYN_SENTtcp_active_pcbs移除,释放控制块,更新mib2统计(连接尝试失败)
    其他状态(如ESTABLISHED调用tcp_close_shutdown_fin()发送FIN报文,启动关闭流程(四次挥手)
  • 特殊逻辑:若存在未确认数据,可能发送RST报文强制关闭连接,避免数据残留。

7. 接收数据:tcp_recv()

  • 核心功能:为 TCP 控制块注册 “数据接收回调函数”,内核收到数据后触发回调,将数据递交给应用层。
  • 实现逻辑
    1. 注册tcp_recv_fn类型回调函数到控制块的recv字段;
    2. 回调触发场景:
      • 正常接收数据:ppbuf指针,指向接收数据)非空,应用层需处理数据;
      • 对方关闭连接:p为空,应用层需调用tcp_close()关闭本地连接;
    3. 调用时机:通常在tcp_connected_fn(连接建立回调)中注册,确保连接稳定后可接收数据。

8. 发送确认回调:tcp_sent()

  • 核心功能:注册 “发送确认回调函数”,当发送的数据被对方确认(收到ACK)时,内核触发回调。
  • 作用:告知应用层 “数据已送达”,可用于清理发送缓冲区、触发下一次数据发送。
  • 回调参数len(被确认的数据长度)、tpcb(对应的 TCP 控制块)。

9. 异常处理:tcp_err()

  • 核心功能:注册 “异常处理回调函数”,当 TCP 连接发生异常(如连接超时、对方重置)时,内核触发回调。
  • 用户职责:在回调中实现异常恢复逻辑(如释放控制块、重新连接)。
  • 源码核心
    typedef void (*tcp_err_fn)(void *arg, err_t err);
    void tcp_err(struct tcp_pcb *pcb, tcp_err_fn err) { if (pcb) pcb->errf = err; }
    

10. 周期性回调:tcp_poll()

  • 核心功能:注册 “周期性回调函数”,内核每interval * 0.5s(内核定时器周期为 0.5s)触发一次,解决裸机编程中周期性任务的实现难题。
  • 典型用途:周期性检测连接状态、定时发送心跳包、清理超时数据。
  • 参数说明interval(周期系数,如interval=2表示每 1s 触发一次)、tcp_poll_fn(周期性回调函数)。

11. 构建 TCP 报文段:tcp_write()

  • 核心功能:构建 TCP 报文段并缓存到控制块的unsent字段(仅 RAW API 需直接调用,上层 API 已封装)。
  • 关键细节
    1. 不直接发送数据:仅完成报文段构建和缓存,真正发送需依赖内核超时机制调用tcp_output(),或用户手动调用tcp_output()立即发送;
    2. 参数说明:
      • dataptr:待发送数据的指针;
      • len:数据长度(字节);
      • apiflags:额外操作标志(如是否拷贝数据、是否设置PSH标志);
    3. 前置检查:调用tcp_write_checks()验证数据是否可挂载到发送缓冲区,避免溢出。

12. 更新接收窗口:tcp_recved()

  • 核心功能:告知内核 “应用层已处理接收数据”,更新接收窗口大小,避免发送方因窗口为 0 而停止发送。
  • 实现逻辑
    1. 接收窗口增加len(应用层已处理的数据长度),若窗口超过最大值则截断为TCP_WND_MAX
    2. 调用tcp_update_rcv_ann_wnd()更新窗口通告,若窗口增量≥TCP_WND_UPDATE_THRESHOLD(通常为窗口的 1/4),立即发送ACK告知发送方新窗口;
  • 注意事项:若不调用此函数,接收窗口会逐渐变为 0,导致后续数据无法接收(常见排障点)。

核心总结:RAW API TCP 编程流程

服务器端流程

  1. tcp_new() → 新建控制块;
  2. tcp_bind() → 绑定本地 IP + 端口;
  3. tcp_listen() → 转为监听状态,插入tcp_listen_pcbs
  4. tcp_accept() → 注册连接回调,等待客户端连接;
  5. 连接建立后(回调触发):tcp_recv()(注册接收回调)、tcp_sent()(注册发送确认回调);
  6. 数据交互:tcp_write()+tcp_output()(发送数据)、tcp_recved()(更新接收窗口);
  7. 关闭连接:tcp_close()

客户端流程

  1. tcp_new() → 新建控制块;
  2. tcp_bind()(可选)→ 绑定本地 IP + 端口(不绑定则自动分配);
  3. tcp_connect() → 发起连接,注册连接回调;
  4. 连接建立后(回调触发):tcp_recv()tcp_sent()
  5. 数据交互:同服务器端;
  6. 关闭连接:tcp_close()
http://www.dtcms.com/a/354702.html

相关文章:

  • 数据结构8---排序
  • 鸿蒙OS与Rust整合开发流程
  • 【边缘计算】RK3576算力评估
  • 排序(Sort)方法详解(冒泡、插入、希尔、选择、堆、快速、归并)
  • 详细介绍Linux 内存管理 struct page数据结构中有一个锁,请问trylock_page()和lock_page()有什么区别?
  • 开源工具新玩法:cpolar提升Penpot协作流畅度
  • 8.28日QT
  • 分布式锁过期危机:4大续命方案拯救超时任务
  • 2025年机械工程与机器人国际研讨会(CMER2025)
  • PAT 1086 Tree Traversals Again
  • React 动画库
  • 2025.8.28总结
  • Docker Swarm vs Kubernetes vs Nomad:容器编排方案对比与选型建议
  • GitHub宕机自救指南技术文章大纲
  • 图论基础篇
  • Oracle 数据库权限管理的艺术:从入门到精通
  • 【第四章】BS 架构测试全解析:从功能验证到问题定位​
  • @HAProxy 介绍部署使用
  • DM LSN 与 Oracle SCN 对比
  • UNIX网络编程笔记:共享内存区和远程过程调用
  • 机器学习基本概述
  • 小白入门:支持深度学习的视觉数据库管理系统
  • 神经网络为何能 “学习”?从神经元到深度学习模型的层级结构解析
  • 【OS】IO
  • 不同业务怎么选服务器?CPU / 内存 / 带宽配置表
  • [肥用云计算] Serverless 多环境配置
  • 【SpringBoot 版本升级整合Redis异常解决】Unable to connect to 127.0.0.1:6379
  • 云计算学习100天-第32天
  • InnoDB存储引擎底层拆解:从页、事务到锁,如何撑起MySQL数据库高效运转(上)
  • 音频转PCM