第一部分 基础分析

/**
 * @file http_middleware_analysis.c
 * @brief HTTP 中间件从请求到响应的完整链路分析
 * 
 * 分析范围:
 * 1. HTTP 协议栈架构(OSI 七层模型视角)
 * 2. 开源 HTTP 中间件源码树形分析(Nginx/Envoy/HAProxy)
 * 3. 请求处理全链路追踪
 * 4. 内存管理/缓冲区/连接池
 * 5. 事件驱动模型(Reactor/Proactor)
 * 6. 性能分析与调优
 * 7. 常见问题定位与调试
 */

一、HTTP 中间件架构全景

1.1 OSI 七层模型视角

【HTTP 中间件在协议栈中的位置】
​
┌─────────────────────────────────────────────────────────────────┐
│                    应用层 (Application Layer)                    │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    HTTP/HTTPS/HTTP2/HTTP3               │   │
│  │              (请求解析、路由、业务逻辑、响应生成)          │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    表示层 (Presentation Layer)                   │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │              TLS/SSL (加密/解密、证书验证)               │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    会话层 (Session Layer)                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │           Session/Cookie 管理、Keep-Alive              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    传输层 (Transport Layer)                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    TCP/UDP (连接管理)                    │   │
│  │         (Nginx/Envoy/HAProxy 核心处理层)                │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    网络层 (Network Layer)                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                     IP (路由)                            │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    数据链路层 (Data Link Layer)                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    Ethernet                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                              ↓                                  │
├─────────────────────────────────────────────────────────────────┤
│                    物理层 (Physical Layer)                     │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                    网卡/光纤                             │   │
│  └─────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

二、开源 HTTP 中间件源码树形分析

2.1 Nginx 源码目录树

Nginx 源码结构 (nginx-1.24.0)
├── auto/                          # 自动编译脚本
│   ├── cc/                        # 编译器检测
│   ├── feature/                   # 功能检测
│   ├── lib/                       # 库检测
│   └── options                    # 编译选项解析
│
├── src/                           # 核心源码 ★
│   ├── core/                      # 核心基础模块
│   │   ├── nginx.c                # 主入口函数 (main) ★
│   │   ├── ngx_cycle.c            # 主循环/生命周期管理 ★
│   │   ├── ngx_connection.c       # 连接管理 ★
│   │   ├── ngx_buf.c              # 缓冲区管理 ★
│   │   ├── ngx_array.c            # 动态数组
│   │   ├── ngx_list.c             # 链表
│   │   ├── ngx_queue.c            # 队列
│   │   ├── ngx_hash.c             # 哈希表
│   │   ├── ngx_string.c           # 字符串处理
│   │   ├── ngx_file.c             # 文件操作
│   │   ├── ngx_log.c              # 日志系统
│   │   ├── ngx_palloc.c           # 内存池管理 ★
│   │   ├── ngx_radix_tree.c       # 基数树
│   │   ├── ngx_open_file_cache.c  # 文件缓存
│   │   └── ngx_rbtree.c           # 红黑树
│   │
│   ├── event/                     # 事件处理模块 ★
│   │   ├── ngx_event.c            # 事件核心 ★
│   │   ├── ngx_event_accept.c     # 接受连接
│   │   ├── ngx_event_connect.c    # 连接后端
│   │   ├── ngx_event_pipe.c       # 事件管道
│   │   ├── ngx_event_timer.c      # 定时器管理
│   │   ├── ngx_event_posted.c     # 发布事件
│   │   ├── modules/               # 事件模块
│   │   │   ├── ngx_epoll_module.c   # epoll (Linux)
│   │   │   ├── ngx_kqueue_module.c  # kqueue (BSD/macOS)
│   │   │   ├── ngx_iocp_module.c    # IOCP (Windows)
│   │   │   ├── ngx_select_module.c  # select
│   │   │   └── ngx_poll_module.c    # poll
│   │   └── ...
│   │
│   ├── http/                      # HTTP 模块 ★
│   │   ├── ngx_http.c             # HTTP 核心初始化
│   │   ├── ngx_http_request.c     # HTTP 请求处理 ★
│   │   ├── ngx_http_parse.c       # HTTP 请求解析 ★
│   │   ├── ngx_http_header_filter.c  # 响应头过滤
│   │   ├── ngx_http_write_filter.c   # 响应写过滤
│   │   ├── ngx_http_core_module.c    # HTTP 核心模块
│   │   ├── ngx_http_variables.c      # 变量处理
│   │   ├── ngx_http_upstream.c       # 上游/代理 ★
│   │   ├── ngx_http_upstream_round_robin.c  # 负载均衡
│   │   ├── modules/               # HTTP 模块
│   │   │   ├── ngx_http_proxy_module.c    # 代理模块
│   │   │   ├── ngx_http_fastcgi_module.c  # FastCGI
│   │   │   ├── ngx_http_uwsgi_module.c    # uWSGI
│   │   │   ├── ngx_http_scgi_module.c     # SCGI
│   │   │   ├── ngx_http_memcached_module.c # Memcached
│   │   │   ├── ngx_http_limit_conn_module.c  # 连接限制
│   │   │   ├── ngx_http_limit_req_module.c   # 请求限制
│   │   │   ├── ngx_http_rewrite_module.c     # 重写
│   │   │   ├── ngx_http_ssl_module.c         # SSL/TLS
│   │   │   ├── ngx_http_gzip_module.c        # 压缩
│   │   │   └── ngx_http_static_module.c      # 静态文件
│   │   └── ...
│   │
│   ├── stream/                    # 流模块 (TCP/UDP代理)
│   │   ├── ngx_stream.c
│   │   ├── ngx_stream_core_module.c
│   │   ├── ngx_stream_proxy_module.c
│   │   └── modules/
│   │
│   ├── mail/                      # 邮件模块 (POP3/IMAP/SMTP)
│   │   ├── ngx_mail.c
│   │   └── modules/
│   │
│   ├── os/                        # 操作系统相关
│   │   ├── unix/                  # Unix/Linux 实现
│   │   │   ├── ngx_os.h
│   │   │   ├── ngx_linux.h
│   │   │   ├── ngx_posix_init.c
│   │   │   ├── ngx_process.c
│   │   │   ├── ngx_process_cycle.c   # 进程管理 ★
│   │   │   └── ngx_socket.c
│   │   └── win32/                 # Windows 实现
│   │
│   └── misc/                      # 杂项
│       └── ngx_cpp_test_module.cpp
│
├── contrib/                       # 贡献脚本
│   ├── geo2nginx.pl
│   ├── unicode2nginx/
│   └── vim/
│
├── conf/                          # 默认配置示例
│   ├── nginx.conf                 # 主配置文件
│   ├── mime.types                 # MIME 类型映射
│   ├── fastcgi_params
│   ├── proxy_params
│   ├── uwsgi_params
│   └── scgi_params
│
├── html/                          # 默认网页
│   ├── index.html
│   └── 50x.html
│
├── man/                           # 帮助手册
│   └── nginx.8
│
└── configure                      # 编译配置脚本

2.2 Nginx 核心模块调用树

【Nginx 主流程函数调用树】
​
main() [nginx.c]
├── ngx_strerror_init()                 # 初始化错误字符串
├── ngx_get_options()                   # 解析命令行参数
├── ngx_time_init()                     # 初始化时间
├── ngx_log_init()                      # 初始化日志
├── ngx_memalign()                      # 内存对齐分配
├── ngx_init_cycle()                    # 初始化主循环 ★
│   ├── ngx_timezone_update()           # 时区更新
│   ├── ngx_conf_parse()                # 解析配置文件 ★
│   │   └── ngx_conf_handler()          # 处理配置指令
│   ├── ngx_create_pool()               # 创建内存池
│   ├── ngx_open_listening_sockets()    # 打开监听socket
│   ├── ngx_event_core_init()           # 事件核心初始化
│   ├── ngx_event_process_init()        # 事件处理初始化
│   └── ngx_http_init()                 # HTTP模块初始化
│
├── ngx_single_process_cycle() / ngx_master_process_cycle()
│   ├── ngx_start_worker_processes()    # 启动worker进程
│   │   └── ngx_spawn_process()         # 创建子进程
│   │       └── ngx_worker_process_cycle()  # worker主循环
│   │           ├── ngx_process_events_and_timers()  # 事件循环 ★
│   │           │   ├── ngx_event_find_timer()       # 查找定时器
│   │           │   ├── ngx_process_events()         # 处理事件(epoll_wait)
│   │           │   │   └── ngx_epoll_process_events()  # epoll实现
│   │           │   │       ├── epoll_wait()         # 等待事件
│   │           │   │       └── ngx_event_accept()   # 接受连接
│   │           │   │           └── ngx_http_init_connection()
│   │           │   │               └── ngx_http_process_request_line()
│   │           │   │                   ├── ngx_http_parse_request_line()  # 解析请求行
│   │           │   │                   └── ngx_http_process_request_headers() # 解析头部
│   │           │   │                       └── ngx_http_process_request()
│   │           │   │                           ├── ngx_http_handler()      # 业务处理
│   │           │   │                           │   ├── ngx_http_core_run_phases() # 阶段处理
│   │           │   │                           │   │   ├── NGX_HTTP_POST_READ_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_SERVER_REWRITE_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_FIND_CONFIG_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_REWRITE_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_POST_REWRITE_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_PREACCESS_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_ACCESS_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_POST_ACCESS_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_TRY_FILES_PHASE
│   │           │   │                           │   │   ├── NGX_HTTP_CONTENT_PHASE    # ★ 内容生成
│   │           │   │                           │   │   │   ├── ngx_http_proxy_handler()   # 代理
│   │           │   │                           │   │   │   ├── ngx_http_fastcgi_handler()  # FastCGI
│   │           │   │                           │   │   │   ├── ngx_http_static_handler()   # 静态文件
│   │           │   │                           │   │   │   └── ngx_http_index_handler()    # 索引
│   │           │   │                           │   │   └── NGX_HTTP_LOG_PHASE
│   │           │   │                           └── ngx_http_output_filter() # 输出过滤
│   │           │   │                               ├── ngx_http_header_filter()   # 头部过滤
│   │           │   │                               ├── ngx_http_write_filter()    # 写过滤
│   │           │   │                               └── ngx_http_send_special()    # 发送响应
│   │           │   └── ngx_event_expire_timers()   # 过期定时器
│   │           ├── ngx_quit = 1?                  # 检查退出标志
│   │           └── ngx_reopen = 1?                # 检查日志重开
│   │
│   └── ngx_signal_handler()            # 信号处理
│
└── ngx_master_process_exit()           # 退出清理

2.3 Envoy 源码目录树

Envoy 源码结构 (envoy-1.28.0)
├── bazel/                          # Bazel 编译配置
├── ci/                             # CI/CD 配置
├── configs/                        # 示例配置
├── docs/                           # 文档
├── generated/                      # 生成代码
├── include/                        # 公共头文件 ★
│   └── envoy/
│       ├── api/                    # API 定义
│       ├── common/                 # 公共类型
│       ├── config/                 # 配置
│       ├── event/                  # 事件/调度 ★
│       │   ├── dispatcher.h        # 事件分发器
│       │   ├── file_event.h        # 文件事件
│       │   ├── timer.h             # 定时器
│       │   └── signal.h            # 信号
│       ├── http/                   # HTTP 处理 ★
│       │   ├── codec.h             # HTTP 编解码器
│       │   ├── filter.h            # 过滤器接口
│       │   ├── header_map.h        # 头部映射
│       │   └── router.h            # 路由
│       ├── network/                # 网络层
│       │   ├── connection.h        # 连接
│       │   ├── listener.h          # 监听器
│       │   ├── filter.h            # 网络过滤器
│       │   └── socket.h            # Socket
│       ├── server/                 # 服务器核心
│       │   ├── configuration.h     # 配置
│       │   ├── instance.h          # 实例
│       │   ├── options.h           # 启动选项
│       │   └── worker.h            # 工作线程
│       ├── upstream/               # 上游/负载均衡
│       │   ├── cluster_manager.h   # 集群管理
│       │   ├── load_balancer.h     # 负载均衡器
│       │   ├── health_checker.h    # 健康检查
│       │   └── host.h              # 主机
│       ├── stats/                  # 统计
│       │   ├── stats.h
│       │   └── scope.h
│       ├── access_log/             # 访问日志
│       ├── admin/                  # 管理接口
│       ├── extensions/             # 扩展
│       └── thread/                 # 线程
│           └── thread.h
│
├── source/                         # 源文件 ★
│   ├── common/                     # 公共实现
│   │   ├── event/                  # 事件实现
│   │   │   ├── dispatcher_impl.cc  # 调度器实现
│   │   │   ├── file_event_impl.cc
│   │   │   ├── timer_impl.cc
│   │   │   └── signal_impl.cc
│   │   ├── http/                   # HTTP 实现
│   │   │   ├── codec_impl.cc       # HTTP/1.1/2 编解码
│   │   │   ├── header_map_impl.cc  # 头部管理
│   │   │   ├── filter_manager.cc   # 过滤器管理
│   │   │   └── router.cc           # 路由逻辑
│   │   ├── network/                # 网络实现
│   │   │   ├── connection_impl.cc  # 连接实现
│   │   │   ├── listener_impl.cc    # 监听器实现
│   │   │   └── socket_impl.cc      # Socket实现
│   │   ├── upstream/               # 上游实现
│   │   │   ├── cluster_manager_impl.cc
│   │   │   ├── load_balancer_impl.cc
│   │   │   └── health_checker_impl.cc
│   │   └── ...
│   │
│   ├── server/                     # 服务器实现
│   │   ├── server.cc               # 主入口 ★
│   │   ├── config_validation.cc
│   │   ├── configuration_impl.cc
│   │   ├── instance_impl.cc
│   │   ├── options_impl.cc
│   │   ├── worker_impl.cc          # 工作线程实现 ★
│   │   └── guarddog_impl.cc        # 守护
│   │
│   ├── extensions/                 # 扩展模块
│   │   ├── filters/                # 过滤器
│   │   │   ├── http/               # HTTP过滤器
│   │   │   │   ├── router/
│   │   │   │   ├── cors/
│   │   │   │   ├── gzip/
│   │   │   │   ├── jwt_authn/
│   │   │   │   └── ...
│   │   │   └── network/            # 网络过滤器
│   │   │       ├── tcp_proxy/
│   │   │       └── ...
│   │   ├── access_loggers/         # 访问日志器
│   │   ├── stat_sinks/             # 统计输出
│   │   ├── transport_sockets/      # 传输层
│   │   │   ├── tls/
│   │   │   └── raw_buffer/
│   │   └── ...
│   │
│   └── exe/                        # 可执行入口
│       ├── main.cc                 # 主函数 ★
│       ├── main_common.cc
│       └── startup.cc
│
├── test/                           # 测试
│   ├── integration/                # 集成测试
│   ├── unit/                       # 单元测试
│   ├── common/                     # 公共测试
│   └── ...
│
└── tools/                          # 工具
    ├── code_format/
    ├── coverage/
    └── ...

三、请求处理全链路追踪

3.1 HTTP 请求处理时序图

【Nginx HTTP 请求处理完整时序图】
​
Client          Master          Worker          Epoll           HTTP Core        Upstream        Backend
  │               │               │               │                 │               │               │
  │  TCP SYN      │               │               │                 │               │               │
  ├──────────────>│               │               │                 │               │               │
  │               │ accept()      │               │                 │               │               │
  │               ├──────────────>│               │                 │               │               │
  │               │               │ epoll_ctl()   │                 │               │               │
  │               │               ├──────────────>│                 │               │               │
  │               │               │               │                 │               │               │
  │  HTTP GET     │               │ epoll_wait()  │                 │               │               │
  ├──────────────>│               │<──────────────┤                 │               │               │
  │               │               │ 返回可读事件   │                 │               │               │
  │               │               │               │                 │               │               │
  │               │               │ recv()        │                 │               │               │
  │               │               ├──────────────>│                 │               │               │
  │               │               │ 接收请求数据   │                 │               │               │
  │               │               │               │                 │               │               │
  │               │               │ parse_request_line()            │               │               │
  │               │               │─────────────────────────────>   │               │               │
  │               │               │ 解析请求行                      │               │               │
  │               │               │<─────────────────────────────   │               │               │
  │               │               │               │                 │               │               │
  │               │               │ parse_request_headers()         │               │               │
  │               │               │─────────────────────────────>   │               │               │
  │               │               │ 解析请求头                      │               │               │
  │               │               │<─────────────────────────────   │               │               │
  │               │               │               │                 │               │               │
  │               │               │ find_location()                 │               │               │
  │               │               │─────────────────────────────>   │               │               │
  │               │               │               │                 │               │               │
  │               │               │ run_phases()  │                 │               │               │
  │               │               │─────────────────────────────>   │               │               │
  │               │               │               │                 │               │               │
  │               │               │               │ 匹配 proxy_pass  │               │               │
  │               │               │               │────────────────>│               │               │
  │               │               │               │                 │               │               │
  │               │               │               │                 │ create_upstream()
  │               │               │               │                 │──────────────>│               │
  │               │               │               │                 │               │               │
  │               │               │               │                 │ connect()     │               │
  │               │               │               │                 │──────────────>│               │
  │               │               │               │                 │               │  TCP SYN      │
  │               │               │               │                 │               ├──────────────>│
  │               │               │               │                 │               │               │
  │               │               │               │                 │ send request  │               │
  │               │               │               │                 │──────────────>│──────────────>│
  │               │               │               │                 │               │               │
  │               │               │               │                 │               │  recv response│
  │               │               │               │                 │               │<──────────────│
  │               │               │               │                 │ recv response │               │
  │               │               │               │                 │<──────────────│               │
  │               │               │               │                 │               │               │
  │               │               │ output_filter()                 │               │               │
  │               │               │<─────────────────────────────   │               │               │
  │               │               │               │                 │               │               │
  │               │               │ send()        │                 │               │               │
  │               │               ├──────────────>│                 │               │               │
  │               │               │               │                 │               │               │
  │  HTTP Response│               │               │                 │               │               │
  │<──────────────│               │               │                 │               │               │
  │               │               │               │                 │               │               │
  │  FIN          │               │               │                 │               │               │
  ├──────────────>│               │               │                 │               │               │
  │               │               │ close()       │                 │               │               │
  │               │               ├──────────────>│                 │               │               │

3.2 请求生命周期阶段图

【Nginx HTTP 请求处理阶段 (Phase)】
​
┌─────────────────────────────────────────────────────────────────┐
│                    NGX_HTTP_POST_READ_PHASE                     │
│                    (读取请求体后,内容处理前)                     │
│                    典型模块: realip                              │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                  NGX_HTTP_SERVER_REWRITE_PHASE                  │
│                      (server块重写规则)                          │
│                    典型模块: rewrite (server级)                  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   NGX_HTTP_FIND_CONFIG_PHASE                    │
│                     (查找匹配的location)                         │
│                    核心阶段,不挂载模块                           │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   NGX_HTTP_REWRITE_PHASE                        │
│                    (location块重写规则)                          │
│                    典型模块: rewrite (location级)                │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                  NGX_HTTP_POST_REWRITE_PHASE                    │
│                      (重写后处理)                                │
│                 内部跳转处理,不挂载模块                          │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   NGX_HTTP_PREACCESS_PHASE                      │
│                      (访问前检查)                                │
│              典型模块: limit_conn, limit_req                    │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                     NGX_HTTP_ACCESS_PHASE                       │
│                        (访问控制)                                │
│              典型模块: access, auth_basic, auth_request         │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                  NGX_HTTP_POST_ACCESS_PHASE                     │
│                      (访问后处理)                                │
│                    典型模块: satisfied (satisfy any)            │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                  NGX_HTTP_TRY_FILES_PHASE                       │
│                      (try_files处理)                             │
│                    核心阶段,不挂载模块                           │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    NGX_HTTP_CONTENT_PHASE                       │
│                    (内容生成阶段) ★核心                         │
│          典型模块: proxy, fastcgi, static, index               │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                      NGX_HTTP_LOG_PHASE                         │
│                        (日志记录)                                │
│                    典型模块: access_log                         │
└─────────────────────────────────────────────────────────────────┘

四、内存管理与缓冲区

4.1 Nginx 内存池架构

/**
 * Nginx 内存池 (ngx_pool_t) 结构
 * 源码位置: src/core/ngx_palloc.c
 */
​
/* 内存池数据结构 */
typedef struct ngx_pool_s {
    ngx_pool_data_t d;           /* 数据块信息 */
    size_t max;                   /* 最大分配大小 */
    ngx_pool_t *current;          /* 当前使用的内存池 */
    ngx_chain_t *chain;           /* 缓冲区链 */
    ngx_pool_large_t *large;       /* 大块内存链表 */
    ngx_pool_cleanup_t *cleanup;   /* 清理回调 */
    ngx_log_t *log;               /* 日志 */
} ngx_pool_t;
​
/* 内存池数据块 */
typedef struct {
    u_char *last;                 /* 当前可用位置 */
    u_char *end;                  /* 块结束位置 */
    ngx_pool_t *next;             /* 下一个块 */
    ngx_uint_t failed;             /* 分配失败次数 */
} ngx_pool_data_t;
​
/* 大块内存节点 */
typedef struct ngx_pool_large_s {
    ngx_pool_large_t *next;       /* 下一个大块 */
    void *alloc;                   /* 分配的内存指针 */
} ngx_pool_large_t;
​
/* 内存池操作流程 */
┌─────────────────────────────────────────────────────────────────┐
│                     ngx_create_pool(size)                       │
│  分配一个固定大小的内存池 (默认 16KB)                            │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   ngx_palloc(pool, size)                        │
│  分配内存,如果 size > pool->max (4KB),走大块分配               │
└─────────────────────────────────────────────────────────────────┘
          ↓                              ↓
┌──────────────────────┐      ┌──────────────────────────────────┐
│   小块分配 (<4KB)     │      │        大块分配 (≥4KB)           │
│ 从 current 块取内存   │      │  创建 large 节点,独立分配内存    │
│ 不足则创建新块        │      │  链表管理,不释放直到池销毁       │
└──────────────────────┘      └──────────────────────────────────┘
          ↓                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   ngx_destroy_pool(pool)                        │
│  释放所有块、大块内存,调用 cleanup 回调                         │
└─────────────────────────────────────────────────────────────────┘

4.2 Nginx 缓冲区结构

/**
 * Nginx 缓冲区 (ngx_buf_t) 结构
 * 源码位置: src/core/ngx_buf.h
 */
​
typedef struct ngx_buf_s {
    u_char *pos;          /* 数据起始位置 */
    u_char *last;         /* 数据结束位置 */
    off_t file_pos;        /* 文件起始位置 */
    off_t file_last;       /* 文件结束位置 */
​
    u_char *start;        /* 缓冲区起始地址 */
    u_char *end;          /* 缓冲区结束地址 */
​
    ngx_buf_tag_t tag;     /* 缓冲区标签 */
    ngx_file_t *file;      /* 关联的文件 */
​
    /* 标志位 */
    unsigned temporary:1;   /* 临时内存 */
    unsigned memory:1;      /* 内存映射 */
    unsigned mmap:1;        /* mmap映射 */
    unsigned recycled:1;    /* 可回收 */
    unsigned in_file:1;     /* 文件数据 */
    unsigned flush:1;       /* 刷新标记 */
    unsigned sync:1;        /* 同步标记 */
    unsigned last_buf:1;    /* 最后一个缓冲区 */
    unsigned last_in_chain:1; /* 链中最后一个 */
    unsigned shadow:1;       /* 影子缓冲区 */
    unsigned temp_file:1;    /* 临时文件 */
} ngx_buf_t;
​
/* 缓冲区链 (ngx_chain_t) */
struct ngx_chain_s {
    ngx_buf_t *buf;          /* 缓冲区 */
    ngx_chain_t *next;       /* 下一个节点 */
};

4.3 Nginx 缓冲区池

/**
 * 缓冲区池管理 - 避免频繁分配/释放
 */
​
/* 缓冲区池结构 */
typedef struct {
    ngx_chain_t *free;        /* 空闲链表 */
    ngx_chain_t *busy;        /* 使用中链表 */
    ngx_chain_t *out;         /* 输出链表 */
} ngx_chain_pool_t;
​
/* 获取缓冲区链 */
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool)
{
    ngx_chain_t *cl;
    
    /* 从空闲链表取 */
    cl = pool->chain->free;
    if (cl) {
        pool->chain->free = cl->next;
        return cl;
    }
    
    /* 空闲不足,新分配 */
    cl = ngx_palloc(pool, sizeof(ngx_chain_t));
    return cl;
}
​
/* 释放缓冲区链 */
void ngx_free_chain(ngx_pool_t *pool, ngx_chain_t *cl)
{
    /* 放回空闲链表 */
    cl->next = pool->chain->free;
    pool->chain->free = cl;
}

五、事件驱动模型 (Reactor/Proactor)

5.1 Nginx 事件模型架构

/**
 * Nginx 事件模型 (基于 epoll 实现)
 * 源码位置: src/event/ngx_event.c
 */
​
/* 事件模块接口 */
typedef struct {
    int (*add)(ngx_event_t *ev, int event, u_int flags);
    int (*del)(ngx_event_t *ev, int event, u_int flags);
    int (*enable)(ngx_event_t *ev, int event, u_int flags);
    int (*disable)(ngx_event_t *ev, int event, u_int flags);
    int (*add_conn)(ngx_connection_t *c);
    int (*del_conn)(ngx_connection_t *c, u_int flags);
    ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
    ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
​
/* epoll 实现 */
static ngx_event_actions_t ngx_epoll_module_ctx = {
    .add              = ngx_epoll_add_event,
    .del              = ngx_epoll_del_event,
    .enable           = ngx_epoll_enable_event,
    .disable          = ngx_epoll_disable_event,
    .add_conn         = ngx_epoll_add_connection,
    .del_conn         = ngx_epoll_del_connection,
    .process_events   = ngx_epoll_process_events,
    .init             = ngx_epoll_init,
    .done             = ngx_epoll_done,
};
​
/* 事件结构体 */
struct ngx_event_s {
    void *data;                    /* 关联的连接数据 */
    unsigned write:1;              /* 写事件标志 */
    unsigned accept:1;             /* 接受连接标志 */
​
    /* 回调函数 */
    void (*handler)(ngx_event_t *ev);
    
    /* 定时器 */
    ngx_rbtree_node_t timer;
    
    /* 事件标志 */
    unsigned active:1;             /* 事件已注册 */
    unsigned ready:1;              /* 事件就绪 */
    unsigned timedout:1;           /* 已超时 */
    unsigned error:1;              /* 错误 */
    unsigned canceled:1;           /* 已取消 */
};
​
/* 事件循环核心 */
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    ngx_uint_t flags;
    ngx_msec_t timer, delta;
    
    /* 计算定时器超时时间 */
    timer = ngx_event_find_timer();
    
    /* 处理事件 */
    (void) ngx_process_events(cycle, timer, flags);
    
    /* 处理定时器事件 */
    ngx_event_expire_timers();
    
    /* 处理posted事件 */
    ngx_event_process_posted(cycle, &ngx_posted_accept_events);
    ngx_event_process_posted(cycle, &ngx_posted_events);
}

5.2 epoll 事件循环流程图

【Nginx epoll 事件循环】
​
                    ┌─────────────────────────────────────┐
                    │     ngx_epoll_process_events()      │
                    │          (epoll_wait)               │
                    └─────────────────────────────────────┘
                                      ↓
              ┌───────────────────────┴───────────────────────┐
              ↓                                               ↓
    ┌─────────────────────┐                     ┌─────────────────────┐
    │   读事件就绪         │                     │    写事件就绪        │
    │   EPOLLIN           │                     │    EPOLLOUT         │
    └─────────┬───────────┘                     └─────────┬───────────┘
              ↓                                               ↓
    ┌─────────────────────┐                     ┌─────────────────────┐
    │ ngx_event_accept()  │                     │ ngx_http_write_     │
    │   (新连接)          │                     │    filter()         │
    └─────────┬───────────┘                     └─────────┬───────────┘
              ↓                                               ↓
    ┌─────────────────────┐                     ┌─────────────────────┐
    │ ngx_http_init_      │                     │ 发送响应数据        │
    │   connection()      │                     │ (writev/sendfile)   │
    └─────────┬───────────┘                     └─────────┬───────────┘
              ↓                                               ↓
    ┌─────────────────────┐                     ┌─────────────────────┐
    │ ngx_http_process_   │                     │ 数据发完?          │
    │   request_line()    │                     │ close connection   │
    └─────────────────────┘                     └─────────────────────┘
​
【多阶段事件处理】
┌─────────────────────────────────────────────────────────────────┐
│                      事件类型                      │ 处理方式   │
├─────────────────────────────────────────────────────────────────┤
│ 连接建立 (accept)                 │ 由 master 或 worker 处理     │
│ 请求数据可读 (read)               │ 放入 posted_accept_events    │
│ 响应数据可写 (write)              │ 放入 posted_events           │
│ 定时器超时 (timer)                │ 放入 timer_rbtree           │
│ 信号事件 (signal)                 │ 单独信号处理线程             │
└─────────────────────────────────────────────────────────────────┘

5.3 连接池管理

/**
 * Nginx 连接池结构
 * 源码位置: src/core/ngx_connection.h
 */
​
/* 连接结构体 */
typedef struct ngx_connection_s {
    void *data;                    /* 上层数据 */
    ngx_event_t *read;             /* 读事件 */
    ngx_event_t *write;            /* 写事件 */
​
    ngx_socket_t fd;               /* Socket 文件描述符 */
​
    ngx_recv_pt recv;              /* 接收函数 */
    ngx_send_pt send;              /* 发送函数 */
    ngx_recv_chain_pt recv_chain;  /* 链接收 */
    ngx_send_chain_pt send_chain;  /* 链发送 */
​
    ngx_listening_t *listening;    /* 监听器 */
​
    off_t sent;                    /* 已发送字节 */
    ngx_log_t *log;                /* 日志 */
​
    /* 连接状态 */
    unsigned destroyed:1;           /* 已销毁 */
    unsigned log_error:3;           /* 错误日志级别 */
​
    /* 缓冲区 */
    ngx_pool_t *pool;               /* 连接专用内存池 */
    ngx_chain_t *buffer;            /* 缓冲区链 */
} ngx_connection_t;
​
/* 连接池初始化 */
static ngx_connection_t *ngx_cycle->free_connections;  /* 空闲连接链表 */
​
ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log)
{
    ngx_connection_t *c;
    
    /* 从空闲链表取连接 */
    c = ngx_cycle->free_connections;
    if (c == NULL) {
        /* 连接数达到上限,返回错误 */
        return NULL;
    }
    
    /* 移动空闲链表指针 */
    ngx_cycle->free_connections = c->data;
    ngx_cycle->free_connection_n--;
    
    /* 初始化连接 */
    ngx_memzero(c, sizeof(ngx_connection_t));
    c->fd = s;
    c->log = log;
    c->pool = ngx_create_pool(4096, log);  /* 4KB 连接专用池 */
    
    return c;
}

六、性能分析与调优

6.1 性能指标监控

#!/bin/bash
# nginx_perf_monitor.sh - Nginx 性能监控脚本
​
# 1. 连接数统计
echo "=== 连接数统计 ==="
curl -s "http://localhost/nginx_status" 2>/dev/null | grep -E "Active|Reading|Writing|Waiting"
​
# 2. 请求速率统计
echo -e "\n=== 请求速率 ==="
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f2 | sort | uniq -c | tail -10
​
# 3. 响应时间分布
echo -e "\n=== 响应时间分布 ==="
awk '{print $NF}' /var/log/nginx/access.log | cut -d'"' -f2 | \
    awk '{if($1 ~ /^[0-9]+$/) print $1}' | \
    awk '{if($1<100) a++; else if($1<300) b++; else if($1<1000) c++; else d++} \
         END{print "Fast(<100ms): " a; print "Normal(100-300ms): " b; \
              print "Slow(300-1000ms): " c; print "Very Slow(>1000ms): " d}'
​
# 4. 上游服务器健康检查
echo -e "\n=== 上游服务器状态 ==="
curl -s "http://localhost/upstream_status" 2>/dev/null | grep -E "server|status"
​
# 5. 缓存命中率
echo -e "\n=== 缓存命中率 ==="
CACHE_HIT=$(grep "HIT" /var/log/nginx/cache.log | wc -l)
CACHE_MISS=$(grep "MISS" /var/log/nginx/cache.log | wc -l)
TOTAL=$((CACHE_HIT + CACHE_MISS))
if [ $TOTAL -gt 0 ]; then
    HIT_RATE=$((CACHE_HIT * 100 / TOTAL))
    echo "Hit: $CACHE_HIT, Miss: $CACHE_MISS, Hit Rate: $HIT_RATE%"
fi

6.2 内核参数优化

#!/bin/bash
# kernel_tune.sh - Linux 内核参数优化 (适用于高并发HTTP服务)
​
# 1. TCP 参数优化
cat >> /etc/sysctl.conf << 'EOF'
# TCP 连接优化
net.ipv4.tcp_tw_reuse = 1               # TIME_WAIT 复用
net.ipv4.tcp_fin_timeout = 30           # FIN_WAIT 超时
net.ipv4.tcp_max_tw_buckets = 2000000   # TIME_WAIT 最大数量
net.ipv4.tcp_max_syn_backlog = 8192     # SYN 队列长度
net.ipv4.tcp_syn_retries = 2            # SYN 重试次数
net.ipv4.tcp_synack_retries = 2         # SYN-ACK 重试次数
​
# TCP 窗口优化
net.core.rmem_max = 134217728           # 接收缓冲最大 128MB
net.core.wmem_max = 134217728           # 发送缓冲最大 128MB
net.ipv4.tcp_rmem = 4096 87380 134217728
net.ipv4.tcp_wmem = 4096 65536 134217728
​
# 连接队列优化
net.core.somaxconn = 65535              # listen 队列长度
net.core.netdev_max_backlog = 65535      # 输入队列长度
​
# 文件描述符限制
fs.file-max = 2097152                   # 最大文件描述符
EOF
​
# 2. 应用配置
cat >> /etc/security/limits.conf << 'EOF'
* soft nofile 655350
* hard nofile 655350
* soft nproc 655350
* hard nproc 655350
EOF
​
# 3. Nginx 配置优化
cat >> /etc/nginx/nginx.conf << 'EOF'
# 全局配置
worker_processes auto;
worker_rlimit_nofile 655350;
​
events {
    use epoll;
    worker_connections 65535;
    multi_accept on;
    accept_mutex on;
}
​
http {
    # 保持连接优化
    keepalive_timeout 65;
    keepalive_requests 1000;
    
    # 缓冲区优化
    client_body_buffer_size 128k;
    client_header_buffer_size 8k;
    large_client_header_buffers 4 32k;
    
    # 文件缓存
    open_file_cache max=10000 inactive=60s;
    open_file_cache_valid 30s;
    open_file_cache_errors on;
    
    # 输出缓冲
    output_buffers 32 32k;
    postpone_output 1460;
}
EOF

6.3 性能压测工具

#!/bin/bash
# benchmark.sh - HTTP 性能压测
​
# 1. 使用 ab (Apache Bench)
ab -n 100000 -c 1000 -k http://localhost/ \
   -H "Accept-Encoding: gzip" \
   -H "Connection: keep-alive" \
   -s 30 -t 60
​
# 2. 使用 wrk (更现代)
wrk -t 4 -c 100 -d 30s --latency http://localhost/
​
# 3. 使用 hey (Go 实现)
hey -n 100000 -c 1000 -t 30 -m GET http://localhost/
​
# 4. 使用 locust (Python 分布式)
cat > locustfile.py << 'EOF'
from locust import HttpUser, task, between
​
class WebsiteUser(HttpUser):
    wait_time = between(0.1, 0.5)
    
    @task
    def index(self):
        self.client.get("/")
    
    @task(3)
    def api(self):
        self.client.get("/api/status")
EOF
​
locust -f locustfile.py --host=http://localhost --headless -u 100 -r 10 -t 60s

6.4 火焰图分析

#!/bin/bash
# flamegraph.sh - 生成性能火焰图
​
# 1. 安装 perf
sudo apt install linux-tools-common linux-tools-generic
​
# 2. 采样
sudo perf record -F 99 -g -- nginx -s reload
sudo perf record -F 99 -g -p $(pgrep -f "nginx: worker") -- sleep 30
​
# 3. 生成报告
sudo perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flame.svg
​
# 4. 查看
firefox flame.svg
​
# 或者使用更简单的 py-spy
pip install py-spy
sudo py-spy record -o profile.svg -p $(pgrep -f "nginx: worker") -d 30

七、常见问题定位与调试

7.1 调试工具

#!/bin/bash
# nginx_debug.sh - Nginx 调试工具集
​
# 1. 查看编译配置
nginx -V 2>&1
​
# 2. 测试配置文件
nginx -t
​
# 3. 查看错误日志
tail -f /var/log/nginx/error.log
​
# 4. 查看访问日志 (实时)
tail -f /var/log/nginx/access.log
​
# 5. 使用 strace 跟踪系统调用
strace -p $(pgrep -f "nginx: worker") -e trace=network,file -f
​
# 6. 使用 gdb 调试
gdb -p $(pgrep -f "nginx: worker")
​
# 7. 查看内存使用
pmap -x $(pgrep -f "nginx: worker")
​
# 8. 查看打开文件
lsof -p $(pgrep -f "nginx: worker")
​
# 9. 查看网络连接
ss -tunap | grep nginx
​
# 10. 查看请求处理时间 (添加到日志)
cat > /etc/nginx/conf.d/log_format.conf << 'EOF'
log_format timing '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '$request_time $upstream_response_time '
                  '$upstream_addr';
access_log /var/log/nginx/timing.log timing;
EOF

7.2 常见问题排查表

问题现象 可能原因 排查命令 解决方案
连接数过高 慢客户端/后端 ss -s netstat -an 调整 keepalive, 增加 worker
CPU 100% 计算密集/死循环 perf top top -H 优化配置, 升级硬件
内存泄漏 缓冲区未释放 valgrind pmap 检查模块, 重启进程
502 Bad Gateway 后端不可用 tail error.log 检查后端服务, 增加超时
504 Gateway Timeout 后端响应慢 tail access.log 优化后端, 增加超时
大量 TIME_WAIT 短连接频繁 ss -tan state time-wait 开启 tcp_tw_reuse
文件描述符不足 连接数超限 ulimit -n lsof 调整 ulimit
请求排队 worker 不够 nginx -T 查看配置 增加 worker_processes

7.3 性能基准参考

【Nginx 性能基准参考 (单核 Intel Xeon)】
​
┌─────────────────┬─────────────────┬─────────────────┬─────────────────┐
│     场景        │   QPS (静态)    │   QPS (代理)    │   QPS (动态)    │
├─────────────────┼─────────────────┼─────────────────┼─────────────────┤
│ 静态文件        │ 50,000+         │ N/A             │ N/A             │
│ 反向代理        │ 20,000-30,000   │ 15,000-20,000   │ 10,000-15,000   │
│ FastCGI (PHP)   │ 3,000-5,000     │ N/A             │ N/A             │
│ 开启 SSL        │ 3,000-5,000     │ 2,000-3,000     │ 1,500-2,500     │
│ HTTP/2          │ 8,000-10,000    │ 6,000-8,000     │ 4,000-6,000     │
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘
​
【内存占用参考】
┌─────────────────────────────────────────────────────────────────┐
│  配置           │  每个 worker 内存 │  总内存 (4 worker)         │
├─────────────────┼─────────────────┼─────────────────────────────┤
│  最小配置       │  约 10 MB        │  约 50 MB                   │
│  默认配置       │  约 30 MB        │  约 150 MB                  │
│  大量 keepalive │  约 50-100 MB    │  约 200-400 MB              │
│  缓存配置       │  约 100-500 MB   │  约 400 MB - 2 GB           │
└─────────────────┴─────────────────┴─────────────────────────────┘

八、总结

维度 Nginx Envoy HAProxy
架构 多进程 (master+worker) 多线程 (thread-per-core) 多进程 (单线程事件驱动)
事件模型 epoll/kqueue (Reactor) epoll (Reactor) epoll (Reactor)
内存管理 内存池 (ngx_pool_t) 引用计数 + 内存池 固定缓冲区
配置语言 Nginx 声明式 YAML 声明式
HTTP/2 原生支持 原生支持 需要配置
gRPC 支持 原生支持 支持
扩展性 模块 (C) 过滤器 (C++) Lua 脚本
适用场景 Web 服务器、反向代理 云原生、Service Mesh 负载均衡、高可用

HTTP 中间件核心要点

  1. 事件驱动 - 所有开源中间件都基于 epoll/kqueue 实现高并发

  2. 内存池 - Nginx 的内存池设计避免了频繁分配/释放

  3. 零拷贝 - sendfile 技术直接发送文件到 socket

  4. 阶段处理 - Nginx 的 11 个处理阶段实现模块化

  5. 连接复用 - Keep-Alive 大幅提升性能

第二部分 HTTP 中间件嵌入式部署与文件传输全链路分析

/**
 * @file http_embedded_deployment.c
 * @brief HTTP 库在嵌入式目标板的部署结构 + 文件上传/下载全流程
 * 
 * 内容涵盖:
 * 1. libcurl/libmicrohttpd 在嵌入式板子的目录树形结构
 * 2. HTTP 文件上传全流程(C 语言实现 + 树形分析)
 * 3. HTTP 文件下载全流程(C 语言实现 + 树形分析)
 * 4. 断点续传的多种实现手段
 * 5. 通过路由设备与其他 IP 通信(NAT/端口转发/UPnP)
 */

一、HTTP 库在嵌入式目标板的部署结构

1.1 libcurl 部署目录树

【嵌入式目标板 - libcurl 安装目录树】
/
├── usr/
│   ├── lib/
│   │   ├── libcurl.so -> libcurl.so.4.8.0
│   │   ├── libcurl.so.4 -> libcurl.so.4.8.0
│   │   ├── libcurl.so.4.8.0          # 动态库 (约 800KB-1.2MB)
│   │   ├── libcurl.a                  # 静态库 (约 1.5-2.5MB,调试版更大)
│   │   └── pkgconfig/
│   │       └── libcurl.pc             # pkg-config 配置
│   │
│   ├── include/
│   │   └── curl/
│   │       ├── curl.h                 # 主头文件
│   │       ├── curlver.h              # 版本定义
│   │       ├── easy.h                 # 简易接口
│   │       ├── multi.h                # 多接口
│   │       ├── options.h              # 选项定义
│   │       ├── typecheck-gcc.h        # 类型检查
│   │       ├── mprintf.h              # printf 扩展
│   │       ├── stdcheaders.h          # 标准头包含
│   │       └── system.h               # 系统适配
│   │
│   ├── bin/
│   │   └── curl                       # curl 命令行工具 (约 200KB)
│   │
│   └── share/
│       └── man/
│           └── man1/
│               └── curl.1.gz          # 帮助手册
│
├── etc/
│   └── ld.so.conf.d/
│       └── curl.conf                  # 动态链接器配置
│
└── lib/
    └── libcurl.so.4 -> /usr/lib/libcurl.so.4   # 符号链接

1.2 libmicrohttpd 部署目录树

【嵌入式目标板 - libmicrohttpd 安装目录树】
/
├── usr/
│   ├── lib/
│   │   ├── libmicrohttpd.so -> libmicrohttpd.so.12.58.0
│   │   ├── libmicrohttpd.so.12 -> libmicrohttpd.so.12.58.0
│   │   ├── libmicrohttpd.so.12.58.0   # 动态库 (约 400-600KB)
│   │   ├── libmicrohttpd.a             # 静态库 (约 800KB-1.2MB)
│   │   └── pkgconfig/
│   │       └── libmicrohttpd.pc
│   │
│   ├── include/
│   │   └── microhttpd.h                # 主头文件 (MHD API)
│   │
│   ├── bin/
│   │   └── microhttpd-test             # 测试程序 (可选)
│   │
│   └── share/
│       └── doc/
│           └── libmicrohttpd/
│               ├── tutorial.html
│               └── examples/           # 示例代码
│
└── etc/
    └── microhttpd/
        └── mime.types                  # MIME 类型配置

1.3 交叉编译脚本示例

#!/bin/bash
# cross_compile_http.sh - 交叉编译 HTTP 库到 ARM 目标板
​
# 环境变量配置
export TOOLCHAIN=/opt/gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf
export CC=$TOOLCHAIN/bin/arm-none-linux-gnueabihf-gcc
export CXX=$TOOLCHAIN/bin/arm-none-linux-gnueabihf-g++
export AR=$TOOLCHAIN/bin/arm-none-linux-gnueabihf-ar
export LD=$TOOLCHAIN/bin/arm-none-linux-gnueabihf-ld
export STRIP=$TOOLCHAIN/bin/arm-none-linux-gnueabihf-strip
export CFLAGS="-Os -fPIC -flto"
export LDFLAGS="-Wl,-O1 -Wl,--as-needed"
​
# 目标板 sysroot
export SYSROOT=$TOOLCHAIN/arm-none-linux-gnueabihf/libc
export INSTALL_DIR=/tmp/http_libs_install
​
# 1. 编译 OpenSSL (HTTPS 支持)
echo "=== Building OpenSSL ==="
cd /tmp
wget https://www.openssl.org/source/openssl-3.0.12.tar.gz
tar xzf openssl-3.0.12.tar.gz
cd openssl-3.0.12
./Configure linux-generic32 \
    --cross-compile-prefix=arm-none-linux-gnueabihf- \
    --prefix=$INSTALL_DIR/openssl \
    --openssldir=$INSTALL_DIR/openssl/ssl \
    no-shared \
    no-asm
make -j4 && make install
​
# 2. 编译 libcurl (客户端)
echo "=== Building libcurl ==="
cd /tmp
wget https://curl.se/download/curl-8.5.0.tar.gz
tar xzf curl-8.5.0.tar.gz
cd curl-8.5.0
./configure \
    --host=arm-none-linux-gnueabihf \
    --prefix=$INSTALL_DIR/curl \
    --with-sysroot=$SYSROOT \
    --with-ssl=$INSTALL_DIR/openssl \
    --enable-static \
    --disable-shared \
    --disable-ldap \
    --disable-ldaps \
    --disable-rtsp \
    --disable-dict \
    --disable-telnet \
    --disable-tftp \
    --disable-pop3 \
    --disable-imap \
    --disable-smtp \
    --disable-gopher \
    --disable-mqtt \
    --disable-manual \
    --disable-verbose \
    --disable-versioned-symbols \
    --enable-symbol-hiding \
    --without-libidn2 \
    --without-brotli \
    --without-zstd
make -j4 && make install
​
# 3. 编译 libmicrohttpd (服务端)
echo "=== Building libmicrohttpd ==="
cd /tmp
wget https://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-1.0.1.tar.gz
tar xzf libmicrohttpd-1.0.1.tar.gz
cd libmicrohttpd-1.0.1
./configure \
    --host=arm-none-linux-gnueabihf \
    --prefix=$INSTALL_DIR/mhd \
    --with-sysroot=$SYSROOT \
    --enable-static \
    --disable-shared \
    --disable-https \
    --disable-curl \
    --disable-dauth \
    --disable-messages \
    --disable-postprocessor \
    --disable-docs
make -j4 && make install
​
# 4. 打包最终库文件
echo "=== Packaging ==="
cd $INSTALL_DIR
tar czf http_libs_arm.tar.gz curl/ mhd/ openssl/
​
# 5. 部署到目标板
# scp http_libs_arm.tar.gz root@192.168.1.100:/tmp/
# 在目标板上:
# tar xzf /tmp/http_libs_arm.tar.gz -C /usr/

1.4 目标板库依赖检查

# 在目标板上检查库依赖
root@board:~# ls -la /usr/lib/libcurl*
-rw-r--r-- 1 root root 1523456 Mar 25 10:30 libcurl.a
-rwxr-xr-x 1 root root  985432 Mar 25 10:30 libcurl.so.4.8.0
lrwxrwxrwx 1 root root      19 Mar 25 10:30 libcurl.so.4 -> libcurl.so.4.8.0
lrwxrwxrwx 1 root root      19 Mar 25 10:30 libcurl.so -> libcurl.so.4.8.0
​
root@board:~# ldd /usr/lib/libcurl.so.4.8.0
    linux-vdso.so.1 (0x7efdf000)
    libssl.so.3 => /usr/lib/libssl.so.3 (0x7ef8e000)
    libcrypto.so.3 => /usr/lib/libcrypto.so.3 (0x7edc0000)
    libz.so.1 => /usr/lib/libz.so.1 (0x7ed9d000)
    libc.so.6 => /lib/libc.so.6 (0x7ec5c000)
    libdl.so.2 => /lib/libdl.so.2 (0x7ec4e000)
    libpthread.so.0 => /lib/libpthread.so.0 (0x7ec2b000)
    /lib/ld-linux-armhf.so.3 (0x7efee000)
​
root@board:~# ls -la /usr/include/curl/
total 120
drwxr-xr-x 2 root root  4096 Mar 25 10:30 .
drwxr-xr-x 3 root root  4096 Mar 25 10:30 ..
-rw-r--r-- 1 root root 37280 Mar 25 10:30 curl.h
-rw-r--r-- 1 root root  5543 Mar 25 10:30 curlver.h
-rw-r--r-- 1 root root  4265 Mar 25 10:30 easy.h
-rw-r--r-- 1 root root  8521 Mar 25 10:30 multi.h
-rw-r--r-- 1 root root 29624 Mar 25 10:30 options.h
-rw-r--r-- 1 root root  3348 Mar 25 10:30 typecheck-gcc.h
-rw-r--r-- 1 root root  1512 Mar 25 10:30 mprintf.h
-rw-r--r-- 1 root root  1192 Mar 25 10:30 stdcheaders.h
-rw-r--r-- 1 root root  3200 Mar 25 10:30 system.h

二、HTTP 文件上传全流程 (C 语言实现)

2.1 上传流程树形分析

【HTTP 文件上传完整流程树】
​
┌─────────────────────────────────────────────────────────────────┐
│                    1. 初始化阶段 (Upload Init)                    │
├─────────────────────────────────────────────────────────────────┤
│  curl_global_init(CURL_GLOBAL_ALL)                               │
│  ├── 初始化 SSL 库 (OpenSSL)                                     │
│  ├── 初始化 DNS 缓存                                             │
│  └── 初始化内存分配器                                             │
│                                                                   │
│  curl_easy_init()                                                │
│  ├── 分配 CURL 句柄                                              │
│  ├── 创建连接池                                                  │
│  └── 设置默认选项                                                │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    2. 配置阶段 (Upload Config)                   │
├─────────────────────────────────────────────────────────────────┤
│  curl_easy_setopt(CURLOPT_URL, "http://server/upload")          │
│  └── 解析 URL (协议/主机/端口/路径)                              │
│                                                                   │
│  curl_easy_setopt(CURLOPT_UPLOAD, 1L)                            │
│  └── 设置为上传模式                                               │
│                                                                   │
│  curl_easy_setopt(CURLOPT_READFUNCTION, read_callback)           │
│  └── 注册数据读取回调                                              │
│                                                                   │
│  curl_easy_setopt(CURLOPT_READDATA, &upload_ctx)                 │
│  └── 传递文件句柄                                                 │
│                                                                   │
│  curl_easy_setopt(CURLOPT_INFILESIZE_LARGE, file_size)           │
│  └── 设置文件大小 (用于进度显示和服务器验证)                        │
│                                                                   │
│  curl_easy_setopt(CURLOPT_HTTPHEADER, headers)                   │
│  └── 添加自定义头 (Content-Type, X-File-Name)                    │
│                                                                   │
│  curl_easy_setopt(CURLOPT_NOPROGRESS, 0L)                        │
│  curl_easy_setopt(CURLOPT_XFERINFOFUNCTION, progress_callback)   │
│  └── 设置进度回调                                                 │
│                                                                   │
│  curl_easy_setopt(CURLOPT_VERBOSE, 1L)                           │
│  └── 开启调试输出                                                 │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    3. DNS 解析阶段                               │
├─────────────────────────────────────────────────────────────────┤
│  curl_easy_perform() 内部调用                                     │
│  ├── getaddrinfo("server", "80", ...)                           │
│  │   ├── 检查本地 hosts 文件                                      │
│  │   ├── DNS 查询 (UDP 53)                                       │
│  │   └── 获取 IP 地址列表                                         │
│  └── 选择可用 IP (轮询/优先)                                      │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    4. TCP 连接阶段                               │
├─────────────────────────────────────────────────────────────────┤
│  socket(AF_INET, SOCK_STREAM, 0)                                │
│  ├── 创建 socket 文件描述符                                       │
│  └── 设置 TCP_NODELAY (禁用 Nagle)                               │
│                                                                   │
│  connect(fd, (struct sockaddr*)&addr, sizeof(addr))             │
│  ├── 发送 SYN 包                                                 │
│  ├── 等待 SYN-ACK                                                │
│  ├── 发送 ACK                                                    │
│  └── TCP 三次握手完成                                             │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    5. TLS/SSL 握手阶段 (HTTPS)                   │
├─────────────────────────────────────────────────────────────────┤
│  SSL_CTX_new(TLS_client_method())                               │
│  ├── 加载 CA 证书                                                │
│  ├── 设置验证模式                                                 │
│  └── 创建 SSL 会话                                               │
│                                                                   │
│  SSL_connect(ssl)                                                │
│  ├── ClientHello → 发送支持的加密套件                             │
│  ├── ServerHello ← 服务器选择                                     │
│  ├── 证书验证                                                     │
│  ├── 密钥交换 (ECDHE/RSA)                                        │
│  └── ChangeCipherSpec + Finished                                 │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    6. HTTP 请求发送阶段                          │
├─────────────────────────────────────────────────────────────────┤
│  send_request()                                                  │
│  ├── 发送请求行:                                                  │
│  │   PUT /upload/file.dat HTTP/1.1                               │
│  ├── 发送请求头:                                                  │
│  │   Host: server.example.com                                    │
│  │   User-Agent: libcurl/8.5.0                                   │
│  │   Accept: */*                                                 │
│  │   Content-Type: application/octet-stream                      │
│  │   Content-Length: 1048576                                     │
│  │   Expect: 100-continue                                        │
│  │   X-File-Name: test.dat                                       │
│  │   X-File-Size: 1048576                                        │
│  │   X-File-MD5: d41d8cd98f00b204e9800998ecf8427e               │
│  └── 空行 (\r\n)                                                  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    7. 等待 100 Continue (可选)                    │
├─────────────────────────────────────────────────────────────────┤
│  recv() 读取服务器响应                                            │
│  ├── 收到 "HTTP/1.1 100 Continue" → 继续发送                     │
│  └── 收到 "HTTP/1.1 413 Payload Too Large" → 中止               │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    8. 数据发送阶段 (核心)                         │
├─────────────────────────────────────────────────────────────────┤
│  while (bytes_sent < total_size) {                              │
│      ├── read_callback() 从文件读取数据                           │
│      │   ├── fread(buffer, 1, chunk_size, fp)                   │
│      │   └── 返回数据指针和长度                                   │
│      │                                                           │
│      ├── SSL_write(ssl, buffer, len) 或 send(fd, ...)           │
│      │   ├── TCP 发送缓冲区                                       │
│      │   ├── 内核协议栈                                          │
│      │   └── 网卡发送                                            │
│      │                                                           │
│      ├── progress_callback() 更新进度                            │
│      │   └── 计算速度、剩余时间                                    │
│      │                                                           │
│      └── 检查是否中断 (用户取消/网络错误)                          │
│  }                                                               │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    9. 接收响应阶段                                │
├─────────────────────────────────────────────────────────────────┤
│  recv_response()                                                 │
│  ├── 读取状态行:                                                  │
│  │   HTTP/1.1 200 OK                                            │
│  ├── 读取响应头:                                                  │
│  │   Server: nginx/1.24.0                                       │
│  │   Content-Type: application/json                             │
│  │   Location: /files/test.dat                                  │
│  │   X-Upload-Status: success                                   │
│  │   X-File-MD5: d41d8cd98f00b204e9800998ecf8427e               │
│  ├── 读取响应体 (JSON 响应)                                       │
│  │   {"status":"success","url":"/files/test.dat","size":1048576}│
│  └── 关闭连接或复用 (keep-alive)                                  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    10. 清理阶段                                   │
├─────────────────────────────────────────────────────────────────┤
│  curl_easy_cleanup()                                             │
│  ├── 关闭连接                                                    │
│  ├── 释放 SSL 上下文                                              │
│  ├── 释放内存池                                                   │
│  └── 关闭文件句柄                                                 │
│                                                                   │
│  curl_global_cleanup()                                           │
│  └── 释放全局资源                                                 │
└─────────────────────────────────────────────────────────────────┘

2.2 完整 C 语言上传代码

/**
 * http_upload.c - HTTP 文件上传完整实现
 * 编译: arm-none-linux-gnueabihf-gcc -o http_upload http_upload.c -lcurl
 * 运行: ./http_upload http://server/upload ./file.dat
 */
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <time.h>
​
/* 上传上下文 */
typedef struct {
    FILE *fp;               /* 文件指针 */
    size_t total_size;      /* 文件总大小 */
    size_t uploaded;        /* 已上传大小 */
    char filename[256];      /* 文件名 */
    char md5[33];            /* MD5 值 */
    double start_time;       /* 开始时间 */
} upload_context_t;
​
/* 读取回调函数 - 提供给 libcurl 读取数据 */
static size_t read_callback(char *buffer, size_t size, size_t nitems, void *userdata)
{
    upload_context_t *ctx = (upload_context_t *)userdata;
    size_t bytes_to_read = size * nitems;
    size_t bytes_read;
    
    if (!ctx->fp) {
        return 0;
    }
    
    bytes_read = fread(buffer, 1, bytes_to_read, ctx->fp);
    ctx->uploaded += bytes_read;
    
    return bytes_read;
}
​
/* 进度回调函数 */
static int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
                              curl_off_t ultotal, curl_off_t ulnow)
{
    upload_context_t *ctx = (upload_context_t *)clientp;
    double now = (double)time(NULL);
    double elapsed = now - ctx->start_time;
    double speed = (elapsed > 0) ? ctx->uploaded / elapsed : 0;
    int percent = (ctx->total_size > 0) ? (int)(ctx->uploaded * 100 / ctx->total_size) : 0;
    
    printf("\r[%3d%%] %.2f MB / %.2f MB | %.2f KB/s",
           percent,
           ctx->uploaded / (1024.0 * 1024.0),
           ctx->total_size / (1024.0 * 1024.0),
           speed / 1024.0);
    fflush(stdout);
    
    return 0;  /* 返回非0会中止传输 */
}
​
/* 计算文件 MD5 */
static void compute_md5(const char *filename, char *md5_out)
{
    FILE *fp = fopen(filename, "rb");
    if (!fp) return;
    
    /* 使用 OpenSSL MD5 (需要链接 -lssl -lcrypto) */
    // EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
    // EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
    // ...
    
    fclose(fp);
    
    /* 示例: 固定 MD5 (实际应真实计算) */
    strcpy(md5_out, "d41d8cd98f00b204e9800998ecf8427e");
}
​
/* 获取文件大小 */
static size_t get_file_size(const char *filename)
{
    struct stat st;
    if (stat(filename, &st) == 0) {
        return st.st_size;
    }
    return 0;
}
​
/* 构建请求头 */
static struct curl_slist *build_headers(const char *filename, const char *md5, size_t size)
{
    struct curl_slist *headers = NULL;
    char header[256];
    
    /* 添加自定义头 */
    snprintf(header, sizeof(header), "X-File-Name: %s", filename);
    headers = curl_slist_append(headers, header);
    
    snprintf(header, sizeof(header), "X-File-Size: %zu", size);
    headers = curl_slist_append(headers, header);
    
    snprintf(header, sizeof(header), "X-File-MD5: %s", md5);
    headers = curl_slist_append(headers, header);
    
    headers = curl_slist_append(headers, "Content-Type: application/octet-stream");
    headers = curl_slist_append(headers, "Expect: 100-continue");
    
    return headers;
}
​
/**
 * HTTP 文件上传主函数
 * @param url 上传目标 URL
 * @param filename 本地文件路径
 * @return 0 成功,-1 失败
 */
int http_upload_file(const char *url, const char *filename)
{
    CURL *curl;
    CURLcode res;
    upload_context_t ctx = {0};
    struct curl_slist *headers = NULL;
    
    printf("=== HTTP 文件上传 ===\n");
    printf("目标: %s\n", url);
    printf("文件: %s\n", filename);
    
    /* 1. 打开文件 */
    ctx.fp = fopen(filename, "rb");
    if (!ctx.fp) {
        fprintf(stderr, "错误: 无法打开文件 %s\n", filename);
        return -1;
    }
    
    /* 2. 获取文件信息 */
    ctx.total_size = get_file_size(filename);
    strncpy(ctx.filename, filename, sizeof(ctx.filename) - 1);
    compute_md5(filename, ctx.md5);
    ctx.start_time = (double)time(NULL);
    
    printf("大小: %.2f MB\n", ctx.total_size / (1024.0 * 1024.0));
    printf("MD5: %s\n", ctx.md5);
    
    /* 3. 初始化 libcurl */
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (!curl) {
        fclose(ctx.fp);
        fprintf(stderr, "错误: curl_easy_init 失败\n");
        return -1;
    }
    
    /* 4. 构建请求头 */
    headers = build_headers(ctx.filename, ctx.md5, ctx.total_size);
    
    /* 5. 设置 libcurl 选项 */
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    curl_easy_setopt(curl, CURLOPT_READDATA, &ctx);
    curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)ctx.total_size);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback);
    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &ctx);
    
    /* 超时设置 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3600L);
    
    /* 6. 执行上传 */
    printf("\n上传中...\n");
    res = curl_easy_perform(curl);
    
    /* 7. 检查结果 */
    if (res != CURLE_OK) {
        fprintf(stderr, "\n错误: curl_easy_perform 失败: %s\n", curl_easy_strerror(res));
    } else {
        long http_code = 0;
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
        if (http_code == 200 || http_code == 201) {
            printf("\n✓ 上传成功!\n");
        } else {
            printf("\n✗ 服务器返回错误: %ld\n", http_code);
        }
    }
    
    /* 8. 清理 */
    curl_slist_free_all(headers);
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    fclose(ctx.fp);
    
    return (res == CURLE_OK) ? 0 : -1;
}
​
int main(int argc, char *argv[])
{
    if (argc < 3) {
        fprintf(stderr, "用法: %s <url> <filename>\n", argv[0]);
        fprintf(stderr, "示例: %s http://192.168.1.100:8080/upload test.dat\n", argv[0]);
        return 1;
    }
    
    return http_upload_file(argv[1], argv[2]);
}

三、HTTP 文件下载全流程 (C 语言实现)

3.1 下载流程树形分析

【HTTP 文件下载完整流程树】
​
┌─────────────────────────────────────────────────────────────────┐
│                    1. 初始化阶段 (Download Init)                 │
├─────────────────────────────────────────────────────────────────┤
│  curl_global_init(CURL_GLOBAL_ALL)                               │
│  curl_easy_init()                                                │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    2. 配置阶段 (Download Config)                 │
├─────────────────────────────────────────────────────────────────┤
│  curl_easy_setopt(CURLOPT_URL, "http://server/file.dat")        │
│  curl_easy_setopt(CURLOPT_WRITEFUNCTION, write_callback)        │
│  curl_easy_setopt(CURLOPT_WRITEDATA, &download_ctx)             │
│  curl_easy_setopt(CURLOPT_RESUME_FROM_LARGE, resume_position)   │
│  └── 断点续传起点                                                │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    3. 连接 + HTTP 请求                          │
├─────────────────────────────────────────────────────────────────┤
│  GET /file.dat HTTP/1.1                                         │
│  Host: server.example.com                                       │
│  Range: bytes=1048576-                                          │
│  └── 断点续传关键头                                              │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    4. 接收响应                                  │
├─────────────────────────────────────────────────────────────────┤
│  HTTP/1.1 206 Partial Content                                  │
│  Content-Range: bytes 1048576-2097151/2097152                   │
│  Content-Length: 1048576                                        │
│  └── 206 状态码表示部分内容                                       │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    5. 数据接收循环                               │
├─────────────────────────────────────────────────────────────────┤
│  while (下载未完成) {                                            │
│      recv() / SSL_read()                                        │
│      write_callback() 写入文件                                   │
│      ├── fwrite() 到本地文件                                     │
│      ├── 更新已下载大小                                          │
│      └── 保存断点信息 (每 1MB)                                   │
│  }                                                              │
└─────────────────────────────────────────────────────────────────┘

3.2 完整 C 语言下载代码

/**
 * http_download.c - HTTP 文件下载完整实现 (支持断点续传)
 * 编译: arm-none-linux-gnueabihf-gcc -o http_download http_download.c -lcurl
 * 运行: ./http_download http://server/file.dat ./download.dat
 */
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <unistd.h>
​
/* 下载上下文 */
typedef struct {
    FILE *fp;               /* 文件指针 */
    size_t downloaded;      /* 已下载大小 */
    size_t total_size;      /* 总大小 (从 Content-Length 获取) */
    char resume_file[256];  /* 断点信息保存文件 */
    double start_time;      /* 开始时间 */
    int last_percent;       /* 上次进度百分比 */
} download_context_t;
​
/* 写回调函数 - 保存下载数据到文件 */
static size_t write_callback(char *buffer, size_t size, size_t nmemb, void *userdata)
{
    download_context_t *ctx = (download_context_t *)userdata;
    size_t bytes = size * nmemb;
    size_t written;
    
    if (!ctx->fp) {
        return 0;
    }
    
    written = fwrite(buffer, 1, bytes, ctx->fp);
    ctx->downloaded += written;
    
    /* 每 1MB 保存一次断点信息 */
    if (ctx->resume_file[0] && (ctx->downloaded % (1024 * 1024) < bytes)) {
        FILE *resume_fp = fopen(ctx->resume_file, "w");
        if (resume_fp) {
            fprintf(resume_fp, "%zu\n", ctx->downloaded);
            fclose(resume_fp);
        }
    }
    
    /* 显示进度 */
    if (ctx->total_size > 0) {
        int percent = (int)(ctx->downloaded * 100 / ctx->total_size);
        if (percent != ctx->last_percent) {
            double elapsed = (double)time(NULL) - ctx->start_time;
            double speed = (elapsed > 0) ? ctx->downloaded / elapsed : 0;
            printf("\r[%3d%%] %.2f MB / %.2f MB | %.2f KB/s",
                   percent,
                   ctx->downloaded / (1024.0 * 1024.0),
                   ctx->total_size / (1024.0 * 1024.0),
                   speed / 1024.0);
            fflush(stdout);
            ctx->last_percent = percent;
        }
    }
    
    return written;
}
​
/* 获取已下载进度 (从断点文件读取) */
static size_t get_resume_position(const char *resume_file)
{
    FILE *fp = fopen(resume_file, "r");
    size_t pos = 0;
    if (fp) {
        fscanf(fp, "%zu", &pos);
        fclose(fp);
    }
    return pos;
}
​
/* 保存断点信息 */
static void save_resume_info(download_context_t *ctx)
{
    if (ctx->resume_file[0]) {
        FILE *fp = fopen(ctx->resume_file, "w");
        if (fp) {
            fprintf(fp, "%zu\n", ctx->downloaded);
            fclose(fp);
        }
    }
}
​
/* 检查文件是否存在,获取大小 */
static size_t get_existing_file_size(const char *filename)
{
    struct stat st;
    if (stat(filename, &st) == 0) {
        return st.st_size;
    }
    return 0;
}
​
/* 打开文件用于写入 (支持断点续传模式) */
static FILE *open_file_for_resume(const char *filename, size_t resume_pos, int *is_new)
{
    FILE *fp;
    if (resume_pos > 0) {
        /* 断点续传模式: 追加写入 */
        fp = fopen(filename, "ab");
        if (fp) {
            *is_new = 0;
            printf("断点续传: 从 %zu 字节继续\n", resume_pos);
        }
    }
    
    if (!fp) {
        /* 新文件模式 */
        fp = fopen(filename, "wb");
        *is_new = 1;
    }
    
    return fp;
}
​
/**
 * HTTP 文件下载主函数 (支持断点续传)
 * @param url 下载源 URL
 * @param filename 本地保存路径
 * @param resume 是否启用断点续传
 * @return 0 成功,-1 失败
 */
int http_download_file(const char *url, const char *filename, int resume)
{
    CURL *curl;
    CURLcode res;
    download_context_t ctx = {0};
    size_t resume_pos = 0;
    int is_new_file = 1;
    long http_code = 0;
    double content_length = 0;
    char resume_file[256];
    
    printf("=== HTTP 文件下载 ===\n");
    printf("来源: %s\n", url);
    printf("保存: %s\n", filename);
    
    /* 断点续传文件路径 */
    snprintf(ctx.resume_file, sizeof(ctx.resume_file), "%s.resume", filename);
    
    /* 获取断点位置 */
    if (resume) {
        resume_pos = get_resume_position(ctx.resume_file);
        if (resume_pos > 0) {
            /* 验证已下载文件大小 */
            size_t existing = get_existing_file_size(filename);
            if (existing != resume_pos) {
                printf("警告: 断点文件与本地文件大小不匹配,重新开始\n");
                resume_pos = 0;
                unlink(ctx.resume_file);
            }
        }
    }
    
    /* 打开文件 */
    ctx.fp = open_file_for_resume(filename, resume_pos, &is_new_file);
    if (!ctx.fp) {
        fprintf(stderr, "错误: 无法打开文件 %s\n", filename);
        return -1;
    }
    
    /* 如果断点续传,跳转到正确位置 */
    if (resume_pos > 0) {
        fseek(ctx.fp, 0, SEEK_END);
    }
    
    ctx.downloaded = resume_pos;
    ctx.start_time = (double)time(NULL);
    ctx.last_percent = -1;
    
    /* 初始化 libcurl */
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (!curl) {
        fclose(ctx.fp);
        fprintf(stderr, "错误: curl_easy_init 失败\n");
        return -1;
    }
    
    /* 设置选项 */
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);  /* 跟随重定向 */
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);
    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3600L);
    
    /* 断点续传: 设置 Range 头 */
    if (resume_pos > 0) {
        char range[64];
        snprintf(range, sizeof(range), "%zu-", resume_pos);
        curl_easy_setopt(curl, CURLOPT_RANGE, range);
        printf("Range: bytes=%s\n", range);
    }
    
    /* 执行下载 */
    printf("\n下载中...\n");
    res = curl_easy_perform(curl);
    
    /* 获取响应信息 */
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
    curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
    
    if (content_length > 0) {
        ctx.total_size = (size_t)(resume_pos + content_length);
    }
    
    /* 检查结果 */
    if (res == CURLE_OK) {
        if (http_code == 200 || http_code == 206) {
            printf("\n✓ 下载完成!\n");
            /* 下载完成,删除断点文件 */
            unlink(ctx.resume_file);
        } else {
            printf("\n✗ 服务器返回错误: %ld\n", http_code);
        }
    } else {
        if (res == CURLE_PARTIAL_FILE) {
            printf("\n⚠ 部分下载完成,已保存断点信息\n");
            save_resume_info(&ctx);
        } else {
            fprintf(stderr, "\n错误: %s\n", curl_easy_strerror(res));
        }
    }
    
    /* 清理 */
    curl_easy_cleanup(curl);
    curl_global_cleanup();
    fclose(ctx.fp);
    
    return (res == CURLE_OK) ? 0 : -1;
}
​
int main(int argc, char *argv[])
{
    int resume = 0;
    
    if (argc < 3) {
        fprintf(stderr, "用法: %s <url> <filename> [-r]\n", argv[0]);
        fprintf(stderr, "  -r  启用断点续传\n");
        fprintf(stderr, "示例: %s http://192.168.1.100:8080/file.dat download.dat\n", argv[0]);
        fprintf(stderr, "      %s http://192.168.1.100:8080/file.dat download.dat -r\n", argv[0]);
        return 1;
    }
    
    if (argc >= 4 && strcmp(argv[3], "-r") == 0) {
        resume = 1;
    }
    
    return http_download_file(argv[1], argv[2], resume);
}

四、断点续传的多种实现手段

4.1 断点续传手段对比表

┌─────────────────┬───────────────────────────────────────────────────────┐
│   手段          │                    实现原理                           │
├─────────────────┼───────────────────────────────────────────────────────┤
│ HTTP Range      │ 发送 Range: bytes=start- 头,服务器返回 206 状态码      │
│                 │ 最标准,所有现代 HTTP 服务器都支持                      │
├─────────────────┼───────────────────────────────────────────────────────┤
│ FTP REST        │ 发送 REST offset 命令,再 RETR filename               │
│                 │ 传统 FTP 协议支持                                      │
├─────────────────┼───────────────────────────────────────────────────────┤
│ 客户端状态保存   │ 本地记录已下载字节数,写入 .resume 文件                 │
│                 │ 每次启动时读取,设置 Range 头                           │
├─────────────────┼───────────────────────────────────────────────────────┤
│ 服务端分片索引   │ 服务器记录每个客户端的进度,通过 Token 恢复             │
│                 │ 适用于多用户场景,需要服务器支持                        │
├─────────────────┼───────────────────────────────────────────────────────┤
│ 多线程分片下载   │ 将文件分成多个部分,每个线程独立下载                    │
│                 │ 可加速下载,同时支持断点续传                            │
├─────────────────┼───────────────────────────────────────────────────────┤
│ WebDAV          │ 使用 PROPFIND 获取文件大小,使用 GET 带 Range          │
│                 │ 云存储常用                                            │
├─────────────────┼───────────────────────────────────────────────────────┤
│ 自定义协议       │ 客户端发送位置信息,服务器从指定位置发送                │
│                 │ 适用于私有协议                                          │
└─────────────────┴───────────────────────────────────────────────────────┘

4.2 多线程分片下载实现

/**
 * multi_thread_download.c - 多线程分片下载 (支持断点续传)
 * 编译: gcc -o multi_download multi_thread_download.c -lcurl -lpthread
 */
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <curl/curl.h>
​
#define THREAD_COUNT    4           /* 下载线程数 */
#define CHUNK_SIZE      (1024 * 1024)  /* 每块 1MB,用于保存进度 */
​
/* 分片信息 */
typedef struct {
    int id;                     /* 分片 ID */
    size_t start;               /* 起始位置 */
    size_t end;                 /* 结束位置 (0 表示到文件尾) */
    size_t downloaded;          /* 已下载大小 */
    FILE *fp;                   /* 文件指针 */
    char url[256];              /* 下载 URL */
    int completed;              /* 是否完成 */
    pthread_mutex_t lock;       /* 互斥锁 */
} chunk_t;
​
/* 分片下载线程函数 */
static void *download_chunk(void *arg)
{
    chunk_t *chunk = (chunk_t *)arg;
    CURL *curl;
    CURLcode res;
    char range[64];
    size_t local_downloaded = chunk->downloaded;
    FILE *fp;
    
    curl = curl_easy_init();
    if (!curl) return NULL;
    
    /* 设置 URL */
    curl_easy_setopt(curl, CURLOPT_URL, chunk->url);
    
    /* 设置 Range 头 */
    if (chunk->end > 0) {
        snprintf(range, sizeof(range), "%zu-%zu", 
                 chunk->start + local_downloaded, chunk->end);
    } else {
        snprintf(range, sizeof(range), "%zu-", 
                 chunk->start + local_downloaded);
    }
    curl_easy_setopt(curl, CURLOPT_RANGE, range);
    
    /* 设置写回调 */
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_chunk_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, chunk);
    
    /* 打开文件并定位到分片起始位置 */
    fp = fopen("download.dat", "rb+");
    if (fp) {
        fseek(fp, chunk->start + local_downloaded, SEEK_SET);
    } else {
        fp = fopen("download.dat", "wb");
    }
    chunk->fp = fp;
    
    /* 执行下载 */
    res = curl_easy_perform(curl);
    
    if (res == CURLE_OK) {
        pthread_mutex_lock(&chunk->lock);
        chunk->completed = 1;
        pthread_mutex_unlock(&chunk->lock);
    }
    
    fclose(fp);
    curl_easy_cleanup(curl);
    return NULL;
}
​
/* 分片写入回调 */
static size_t write_chunk_data(char *buffer, size_t size, size_t nmemb, void *userdata)
{
    chunk_t *chunk = (chunk_t *)userdata;
    size_t bytes = size * nmemb;
    
    if (chunk->fp) {
        fwrite(buffer, 1, bytes, chunk->fp);
        chunk->downloaded += bytes;
        return bytes;
    }
    return 0;
}
​
/* 启动多线程分片下载 */
int multi_thread_download(const char *url)
{
    pthread_t threads[THREAD_COUNT];
    chunk_t chunks[THREAD_COUNT];
    size_t file_size = get_remote_file_size(url);  /* 获取远程文件大小 */
    size_t chunk_len = file_size / THREAD_COUNT;
    int i;
    
    /* 预分配文件 */
    FILE *fp = fopen("download.dat", "wb");
    ftruncate(fileno(fp), file_size);
    fclose(fp);
    
    /* 创建分片 */
    for (i = 0; i < THREAD_COUNT; i++) {
        chunks[i].id = i;
        chunks[i].start = i * chunk_len;
        chunks[i].end = (i == THREAD_COUNT - 1) ? 0 : (i + 1) * chunk_len;
        chunks[i].downloaded = 0;
        chunks[i].completed = 0;
        strcpy(chunks[i].url, url);
        pthread_mutex_init(&chunks[i].lock, NULL);
        
        pthread_create(&threads[i], NULL, download_chunk, &chunks[i]);
    }
    
    /* 等待所有线程完成 */
    for (i = 0; i < THREAD_COUNT; i++) {
        pthread_join(threads[i], NULL);
    }
    
    return 0;
}

4.3 断点续传的断点文件格式

/**
 * resume_file.h - 断点文件格式定义
 */
​
/* 断点信息结构体 */
typedef struct {
    char url[512];              /* 下载 URL */
    char local_file[256];       /* 本地文件路径 */
    size_t downloaded;          /* 已下载字节数 */
    size_t total_size;          /* 总大小 (0 表示未知) */
    time_t last_update;         /* 最后更新时间 */
    char etag[128];             /* ETag 用于验证文件是否变化 */
    char last_modified[64];     /* Last-Modified 时间 */
    unsigned char md5[16];      /* 已下载部分的 MD5 (可选) */
} resume_info_t;
​
/* 保存断点信息 */
int save_resume_info(const char *filename, resume_info_t *info)
{
    FILE *fp = fopen(filename, "w");
    if (!fp) return -1;
    
    fprintf(fp, "# HTTP Download Resume File\n");
    fprintf(fp, "# Format: key=value\n");
    fprintf(fp, "url=%s\n", info->url);
    fprintf(fp, "local_file=%s\n", info->local_file);
    fprintf(fp, "downloaded=%zu\n", info->downloaded);
    fprintf(fp, "total_size=%zu\n", info->total_size);
    fprintf(fp, "last_update=%ld\n", info->last_update);
    fprintf(fp, "etag=%s\n", info->etag);
    fprintf(fp, "last_modified=%s\n", info->last_modified);
    
    fclose(fp);
    return 0;
}
​
/* 加载断点信息 */
int load_resume_info(const char *filename, resume_info_t *info)
{
    FILE *fp = fopen(filename, "r");
    char line[512];
    
    if (!fp) return -1;
    
    while (fgets(line, sizeof(line), fp)) {
        if (line[0] == '#' || line[0] == '\n') continue;
        
        char *key = strtok(line, "=");
        char *value = strtok(NULL, "\n");
        if (!key || !value) continue;
        
        if (strcmp(key, "url") == 0) strcpy(info->url, value);
        else if (strcmp(key, "local_file") == 0) strcpy(info->local_file, value);
        else if (strcmp(key, "downloaded") == 0) info->downloaded = atoll(value);
        else if (strcmp(key, "total_size") == 0) info->total_size = atoll(value);
        else if (strcmp(key, "last_update") == 0) info->last_update = atol(value);
        else if (strcmp(key, "etag") == 0) strcpy(info->etag, value);
        else if (strcmp(key, "last_modified") == 0) strcpy(info->last_modified, value);
    }
    
    fclose(fp);
    return 0;
}

五、通过路由设备与其他 IP 通信

5.1 NAT 穿透与端口转发架构

【嵌入式设备通过路由器与外网通信架构】
​
┌─────────────────────────────────────────────────────────────────┐
│                        外网客户端                                │
│                      (公网 IP: 203.0.113.50)                    │
└─────────────────────────────────────────────────────────────────┘
                              ↓
                    ┌─────────────────────┐
                    │     互联网          │
                    └─────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                   家庭/企业路由器                                 │
│               (公网 IP: 122.121.100.200)                         │
│               (内网 IP: 192.168.1.1)                             │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │              NAT 表                                        │  │
│  │  ┌─────────────┬─────────────┬─────────────────────────┐ │  │
│  │  │ 外部端口    │ 内部 IP     │ 内部端口                │ │  │
│  │  ├─────────────┼─────────────┼─────────────────────────┤ │  │
│  │  │ 8080 →      │ 192.168.1.100│ 80 (HTTP 服务)         │ │  │
│  │  │ 8081 →      │ 192.168.1.100│ 8081 (上传服务)        │ │  │
│  │  │ 2222 →      │ 192.168.1.100│ 22 (SSH)               │ │  │
│  │  └─────────────┴─────────────┴─────────────────────────┘ │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│                    嵌入式设备                                    │
│               (内网 IP: 192.168.1.100)                           │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  HTTP 服务端 (端口 80)                                     │  │
│  │  HTTP 上传服务 (端口 8081)                                 │  │
│  │  SSH 服务 (端口 22)                                        │  │
│  └───────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

5.2 UPnP 自动端口映射

/**
 * upnp_port_forward.c - 使用 UPnP 自动添加端口映射
 * 编译: gcc -o upnp_port_forward upnp_port_forward.c -lminiupnpc
 */
​
#include <stdio.h>
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h>
​
/**
 * 通过 UPnP 添加端口映射
 * @param external_port 外部端口
 * @param internal_port 内部端口
 * @param protocol 协议 (TCP/UDP)
 * @param description 描述
 * @return 0 成功,-1 失败
 */
int add_upnp_port_mapping(int external_port, int internal_port, 
                          const char *protocol, const char *description)
{
    struct UPNPDev *devlist = NULL;
    struct UPNPUrls urls;
    struct IGDdatas data;
    char lanaddr[64] = {0};
    int ret;
    
    /* 1. 发现 UPnP 设备 */
    devlist = upnpDiscover(2000, NULL, NULL, 0, 0, 2, NULL);
    if (!devlist) {
        fprintf(stderr, "未发现 UPnP 设备\n");
        return -1;
    }
    
    /* 2. 获取 IGD 服务 URL */
    ret = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
    if (ret != 1) {
        fprintf(stderr, "未找到有效的 IGD\n");
        freeUPNPDevlist(devlist);
        return -1;
    }
    
    printf("IGD 发现: %s\n", urls.controlURL);
    printf("本地 IP: %s\n", lanaddr);
    
    /* 3. 添加端口映射 */
    ret = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
                               (char *)protocol, external_port, internal_port,
                               lanaddr, (char *)description, NULL, NULL);
    
    if (ret != 0) {
        fprintf(stderr, "端口映射失败\n");
        return -1;
    }
    
    printf("端口映射成功: %d -> %s:%d (%s)\n", 
           external_port, lanaddr, internal_port, protocol);
    
    /* 4. 清理 */
    FreeUPNPUrls(&urls);
    freeUPNPDevlist(devlist);
    
    return 0;
}
​
/* 删除端口映射 */
int remove_upnp_port_mapping(int external_port, const char *protocol)
{
    struct UPNPDev *devlist = NULL;
    struct UPNPUrls urls;
    struct IGDdatas data;
    char lanaddr[64];
    int ret;
    
    devlist = upnpDiscover(2000, NULL, NULL, 0, 0, 2, NULL);
    if (!devlist) return -1;
    
    ret = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
    if (ret != 1) {
        freeUPNPDevlist(devlist);
        return -1;
    }
    
    ret = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype,
                                   (char *)protocol, external_port, NULL);
    
    FreeUPNPUrls(&urls);
    freeUPNPDevlist(devlist);
    
    return ret;
}
​
int main()
{
    /* 添加 HTTP 端口映射: 外网 8080 -> 内网 80 */
    add_upnp_port_mapping(8080, 80, "TCP", "HTTP Server");
    
    /* 添加上传服务映射: 外网 8081 -> 内网 8081 */
    add_upnp_port_mapping(8081, 8081, "TCP", "Upload Service");
    
    /* 添加 SSH 映射: 外网 2222 -> 内网 22 */
    add_upnp_port_mapping(2222, 22, "TCP", "SSH Remote Access");
    
    printf("按回车键删除映射...\n");
    getchar();
    
    remove_upnp_port_mapping(8080, "TCP");
    remove_upnp_port_mapping(8081, "TCP");
    remove_upnp_port_mapping(2222, "TCP");
    
    return 0;
}

5.3 通过路由的 TCP 穿透 (打洞)

/**
 * tcp_hole_punching.c - TCP 打洞技术
 * 用于两个内网设备通过路由器直接通信
 */
​
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
​
/* 打洞服务器地址 */
#define STUN_SERVER_IP   "123.123.123.100"
#define STUN_SERVER_PORT 3478
​
/* 获取 NAT 映射的外部地址 */
int get_external_address(int *external_port, char *external_ip)
{
    int sock;
    struct sockaddr_in stun_addr, peer_addr;
    char buf[64];
    socklen_t len = sizeof(peer_addr);
    
    /* 连接 STUN 服务器 */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    stun_addr.sin_family = AF_INET;
    stun_addr.sin_port = htons(STUN_SERVER_PORT);
    inet_aton(STUN_SERVER_IP, &stun_addr.sin_addr);
    
    /* 发送 STUN Binding Request */
    sendto(sock, "STUN", 4, 0, (struct sockaddr*)&stun_addr, sizeof(stun_addr));
    
    /* 接收响应,获取外部地址 */
    recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr*)&peer_addr, &len);
    
    *external_port = ntohs(peer_addr.sin_port);
    inet_ntop(AF_INET, &peer_addr.sin_addr, external_ip, 16);
    
    close(sock);
    return 0;
}
​
/* 向对端设备发起 TCP 打洞连接 */
int tcp_hole_punch(const char *peer_ip, int peer_port, int local_port)
{
    int sock;
    struct sockaddr_in addr;
    int ret;
    
    /* 创建 socket */
    sock = socket(AF_INET, SOCK_STREAM, 0);
    
    /* 绑定本地端口 */
    addr.sin_family = AF_INET;
    addr.sin_port = htons(local_port);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(sock, (struct sockaddr*)&addr, sizeof(addr));
    
    /* 并发连接对端 (打洞核心) */
    addr.sin_port = htons(peer_port);
    inet_aton(peer_ip, &addr.sin_addr);
    
    /* 非阻塞 connect */
    fcntl(sock, F_SETFL, O_NONBLOCK);
    connect(sock, (struct sockaddr*)&addr, sizeof(addr));
    
    /* 等待连接成功或失败 (打洞) */
    usleep(100000);  /* 等待 100ms */
    
    /* 尝试 accept (作为服务器) */
    /* ... */
    
    return sock;
}

5.4 路由器配置脚本

#!/bin/bash
# router_config.sh - 路由器端口转发配置 (通过 SSH 配置 OpenWrt)
​
# 配置信息
ROUTER_IP="192.168.1.1"
ROUTER_USER="root"
ROUTER_PASS="password"
DEVICE_IP="192.168.1.100"
​
# 方法1: SSH 登录路由器配置 iptables
ssh $ROUTER_USER@$ROUTER_IP << EOF
    # 添加端口转发规则
    iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination $DEVICE_IP:80
    iptables -t nat -A POSTROUTING -p tcp -d $DEVICE_IP --dport 80 -j MASQUERADE
    
    # 保存配置
    iptables-save > /etc/iptables.rules
EOF
​
# 方法2: 通过 UCI 配置 OpenWrt
ssh $ROUTER_USER@$ROUTER_IP << EOF
    # 添加端口转发 (OpenWrt UCI)
    uci add firewall redirect
    uci set firewall.@redirect[-1].name="HTTP_Upload"
    uci set firewall.@redirect[-1].src="wan"
    uci set firewall.@redirect[-1].proto="tcp"
    uci set firewall.@redirect[-1].src_dport="8080"
    uci set firewall.@redirect[-1].dest_ip="$DEVICE_IP"
    uci set firewall.@redirect[-1].dest_port="80"
    uci commit firewall
    /etc/init.d/firewall restart
EOF

5.5 通信模式选择

【嵌入式设备与其他 IP 通信模式对比】
​
┌─────────────────┬─────────────────────────────────────────────────────┐
│    模式         │                    说明                             │
├─────────────────┼─────────────────────────────────────────────────────┤
│ 端口映射        │ 路由器手动配置 NAT,外网直接访问内网设备              │
│                 │ 适用于需要被外部访问的场景 (Web 服务、API)          │
├─────────────────┼─────────────────────────────────────────────────────┤
│ UPnP 自动映射   │ 设备自动请求路由器开放端口,无需手动配置              │
│                 │ 适用于 P2P、游戏、多媒体服务                         │
├─────────────────┼─────────────────────────────────────────────────────┤
│ 反向连接        │ 设备主动连接外网服务器 (WebSocket/HTTP)              │
│                 │ 最稳定,无需路由器配置,适用于数据上报场景            │
├─────────────────┼─────────────────────────────────────────────────────┤
│ TCP 打洞        │ 通过 STUN 服务器获取公网地址,两设备直接 P2P 通信    │
│                 │ 适用于需要低延迟、高带宽的场景                        │
├─────────────────┼─────────────────────────────────────────────────────┤
│ 内网直连        │ 同一局域网内通过内网 IP 直接通信                      │
│                 │ 最简单、最快,不需要路由器配置                        │
├─────────────────┼─────────────────────────────────────────────────────┤
│ VPN/ZeroTier   │ 虚拟组网,设备如同在同一局域网                         │
│                 │ 适用于跨公网安全通信                                  │
└─────────────────┴─────────────────────────────────────────────────────┘

5.6 反向连接服务器端示例

/**
 * reverse_connect_server.c - 反向连接服务端 (部署在公网)
 * 嵌入式设备主动连接此服务,然后服务端可以下发指令
 */
​
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
​
#define MAX_CLIENTS 100
​
typedef struct {
    int fd;
    char ip[16];
    char device_id[32];
    int online;
} client_t;
​
client_t clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
​
/* 处理设备连接 */
void *handle_device(void *arg)
{
    int client_fd = *(int *)arg;
    char buffer[4096];
    char device_id[32] = {0};
    int idx = -1;
    
    /* 接收设备 ID */
    recv(client_fd, device_id, sizeof(device_id), 0);
    device_id[31] = '\0';
    
    /* 注册设备 */
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (!clients[i].online) {
            clients[i].fd = client_fd;
            strcpy(clients[i].device_id, device_id);
            clients[i].online = 1;
            idx = i;
            break;
        }
    }
    pthread_mutex_unlock(&clients_mutex);
    
    printf("设备 %s 已连接 (fd=%d)\n", device_id, client_fd);
    
    /* 循环接收数据 */
    while (1) {
        ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        if (n <= 0) break;
        buffer[n] = '\0';
        
        printf("[%s] %s\n", device_id, buffer);
        
        /* 处理设备上报的数据 */
        if (strncmp(buffer, "FILE_UPLOAD", 11) == 0) {
            /* 处理文件上传 */
        }
    }
    
    /* 设备断开 */
    pthread_mutex_lock(&clients_mutex);
    clients[idx].online = 0;
    pthread_mutex_unlock(&clients_mutex);
    
    close(client_fd);
    printf("设备 %s 断开\n", device_id);
    
    return NULL;
}
​
/* 向指定设备发送命令 */
int send_to_device(const char *device_id, const char *cmd)
{
    pthread_mutex_lock(&clients_mutex);
    for (int i = 0; i < MAX_CLIENTS; i++) {
        if (clients[i].online && strcmp(clients[i].device_id, device_id) == 0) {
            send(clients[i].fd, cmd, strlen(cmd), 0);
            pthread_mutex_unlock(&clients_mutex);
            return 0;
        }
    }
    pthread_mutex_unlock(&clients_mutex);
    return -1;
}
​
int main()
{
    int server_fd;
    struct sockaddr_in addr;
    
    /* 创建服务端 socket */
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9000);
    addr.sin_addr.s_addr = INADDR_ANY;
    
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 100);
    
    printf("反向连接服务端启动,端口: 9000\n");
    
    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        pthread_t tid;
        pthread_create(&tid, NULL, handle_device, &client_fd);
        pthread_detach(tid);
    }
    
    return 0;
}

六、总结

模块 关键点 文件/工具
库部署 libcurl + libmicrohttpd + OpenSSL 交叉编译脚本
上传流程 curl_easy_perform + read_callback http_upload.c
下载流程 Range 头 + 206 响应 http_download.c
断点续传 resume 文件 + Range 头 multi_thread_download.c
端口映射 UPnP + iptables upnp_port_forward.c
反向连接 设备主动连接公网服务端 reverse_connect_server.c
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐