简介:这是一份面向 C++ 开发者的 AWS SDK for C++ 完整源码包,适合需要在桌面端或服务端应用中集成亚马逊云服务的团队与个人。SDK 覆盖 EC2、S3、DynamoDB 等常用服务客户端,也包含身份验证、HTTP 通信、线程与异步处理、错误调试等模块,能够帮助开发者省去底层协议和鉴权细节,快速构建云原生应用;同时支持 IAM 角色、STS 临时凭证等安全机制,满足不同部署环境下的凭据管理需求。压缩包共 2000 个文件,其中 1022 个头文件与 965 个 C++ 源文件构成核心实现,另有 11 个 txt 与 2 个 md 文档,整体约 97.43MB,目录结构清晰,可按服务模块对照查阅。已有 316 人学习下载。通过源码可以深入理解 AWS 服务调用的封装方式、CMake 依赖管理和多线程设计,也可作为二次开发、源码分析或构建自定义云 SDK 的参考基础,尤其适合希望基于官方 SDK 做深度定制或研究其内部实现的中高级开发者。 说实话,第一次从一个网盘链接里拿到aws-sdk-cpp.zip这种压缩包时,大部分人心里是没底的。解压出来一堆文件夹,里面有aws-cpp-sdk-core、aws-cpp-sdk-s3、aws-cpp-sdk-ec2,看起来像源码又不完全是,想直接编译又不知道从哪下手。这篇文章就围绕这个压缩包展开,讲清楚 AWS SDK for C++ 到底怎么用、怎么编译、怎么接入自己的 CMake 工程,以及 N 个我在实际项目中踩过的坑。内容适合两类人:一是刚接触 AWS 但 C++ 基础不错的同学,二是在已有 C++ 服务里想加云能力的后端开发。我会尽量把流程写细,让读者照着做就能跑通。
1. 解压之前:先搞清 SDK 交付形态与服务范围
1.1 源码包还是二进制包:先判断拿到的是什么
拿到AWS SDK for C++.zip,第一件事不是急着解压,而是先看压缩包体积和顶层目录结构。如果压缩包里有大量.cmake文件、CMakeLists.txt和各个服务的源码目录,那基本是源码包;如果顶层目录直接是include/和lib/,那大概率是预编译包。两者的使用方式完全不同:
- 源码包需要本机编译,好处是能通过
BUILD_ONLY参数只编自己用到的服务,生成体积很小的静态库。 - 预编译包适合快速验证,但要特别注意编译器和运行库版本是否匹配,尤其是 Windows 下 Debug/Release 不匹配的坑特别多。
我建议大多数情况下自己编译源码包。AWS官方在 GitHub 上维护的aws-sdk-cpp仓库,更新频率很高,而且 CMake 已经把这些构建流程做得相当成熟,不存在“必须依赖官方安装器”的说法。源码包本质上就是仓库某个版本的快照,拿到之后先看根目录的README.md和CMakeLists.txt里的最低版本要求。
1.2 AWS SDK for C++ 的核心模块结构与设计思路
这个 SDK 不是一个大而全的单一库,而是拆成了很多独立模块。最底层是aws-cpp-sdk-core,所有服务模块都依赖它,负责 HTTP 请求、签名、凭据管理、内存管理和日志。上面的服务模块则按照 AWS 服务划分,比如aws-cpp-sdk-s3、aws-cpp-sdk-ec2、aws-cpp-sdk-dynamodb,每个模块对应一组客户端类和请求/响应模型对象。
这种模块化设计对 C++ 开发者来说非常关键。实际工程里,十有八九只用到一两个服务,比如只上传文件到 S3,那完全没必要把 EC2、Lambda 这些模块编进来。CMake 里通过-DBUILD_ONLY="s3;core"就能把构建范围收缩到最小。我自己第一次编译时图省事全量构建,结果在低配 Linux 机器上跑了一个多小时,大量时间耗费在编译用不到的模块上。后来学乖了,每次新增服务调用才重新跑一次 CMake,增量编译非常快。
模块化设计也直接决定了代码写法。S3Client、EC2Client这些类都是独立构造的,它们共享Aws::SDKOptions这个全局配置。理解了这个结构,写代码时就不会困惑“为什么每个 main 函数里都要先调用Aws::InitAPI”。因为 core 模块需要统一初始化全局的 HTTP 客户端、线程池和内存分配器,这是 C++ SDK 的一个设计特点,也经常被吐槽“重”,但它换来的是一致的行为和可控的资源释放。
2. 编译环境准备:依赖库、CMake、编译器一个不少
2.1 编译器版本与 CMake 配置要点
AWS SDK for C++ 从较早的版本开始就要求 C++ 11 标准,但实际使用中建议直接用 C++ 17 或更新版本。原因很简单:这个 SDK 的异步回调接口和标准库容器交互非常频繁,新的 C++ 标准能少写大量样板代码。在 GCC 上,版本低于 7 的编译器基本没法编译新版 SDK,因为模板特化能力和标准库支持都不够。Clang 的话,建议 10 以上。Windows 上则避开老旧的 MSVC 2015,老老实实用 2019 或 2022。
CMake 建议 3.15 以上,因为新版 SDK 的构建脚本用到了一些较新的target_link_options和FetchContent特性。如果系统自带 CMake 版本太低,别再死磕了,直接装新版。另外,构建 Release 版时务必设置-DCMAKE_BUILD_TYPE=Release,否则 SDK 自身会带上大量调试符号,编译慢、运行也慢,最终链接出的二进制体积还会翻几倍。
提示:如果是在容器或者 CI 环境里编译,记得先把基础依赖装齐,避免编译到一半因为找不到头文件报错。Linux 上主要缺的是
libcurl4-openssl-dev、libssl-dev、zlib1g-dev。
2.2 依赖库:curl、OpenSSL、zlib 的细节问题
这个 SDK 的 HTTP 客户端默认基于 libcurl,签名计算又依赖 OpenSSL,压缩相关功能依赖 zlib。如果你只是做 S3 上传下载,这三个依赖基本是绕不开的。版本方面,OpenSSL 1.1.1 和 3.x 都支持,但需要注意一个细节:如果系统中同时存在多个 OpenSSL 版本,CMake 可能找错路径,最终链接出运行时 报libssl.so.1.1找不到的问题。
我的经验是,在 CMake 配置阶段就明确指定依赖路径,比如-DCMAKE_PREFIX_PATH=/usr/local/ssl,或者直接把依赖装进/usr/local下,让 CMake 查找时不产生歧义。zlib 一般不会出问题,但 Windows 上如果用的预编译包,要确认压缩包里的 zlib 是动态库还是静态库,混用的话,运行时会出现zlib1.dll缺失的经典报错。
2.3 裁剪编译:用 BUILD_ONLY 控制构建规模
这个参数值得单独写。BUILD_ONLY是 AWS SDK 提供的一个 CMake 开关,作用是只构建列出的服务模块,不构建全部。命令形式是:
cmake -DBUILD_ONLY="s3;core" -DENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE=Release ..这里有几个隐藏细节。第一,core必须带上,因为所有模块都依赖它,不写也没关系,SDK 会自动依赖,但写了更明确。第二,ENABLE_TESTING一定要关掉,否则它会尝试下载额外的测试依赖,既浪费时间又容易失败。第三,如果你用到 S3 的加密功能,可能还需要追加kms模块,否则某些带 KMS 加密的对象无法正常下载。
编译单元的数量从几十个缩减到五六个之后,整个编译时间能控制在一两分钟内。这个优化对后续迭代开发相当重要,毕竟没人想在改一行代码后等待十几分钟的编译。
3. 构建与集成:从源码到可执行程序的完整链路
3.1 Linux 下从源码编译 AWS SDK 的完整步骤
这里以 Ubuntu 20.04 环境为例,完整走一遍。首先要确保依赖装齐:
sudo apt update sudo apt install -y build-essential cmake libcurl4-openssl-dev libssl-dev zlib1g-dev然后解压源码包,创建构建目录并执行 CMake:
unzip aws-sdk-cpp.zip cd aws-sdk-cpp mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release \ -DBUILD_ONLY="s3;core" \ -DENABLE_TESTING=OFF \ -DCMAKE_INSTALL_PREFIX=/usr/local \ .. make -j$(nproc) sudo make install编译完成后,头文件会安装到/usr/local/include/aws/,库文件在/usr/local/lib/。这样系统里就有了可被 CMake 找到的 AWS SDK 库。make -j$(nproc)这里的nproc是获取 CPU 核心数,用于并行编译,但并行度过高可能内存暴涨,8G 内存以下的机器建议-j2。
注意:如果遇到
fatal error: aws/core/Aws.h: No such file or directory,十有八九不是 SDK 没装好,而是你工程里的 CMake 没有把/usr/local/include加到搜索路径。
3.2 Windows 上用 vcpkg 或预编译包接入
Windows 下最省心的方式是 vcpkg。也许你已经把 vcpkg 配置好了,那么一条命令就能装好:
vcpkg install aws-sdk-cpp[s3]这个 triplet 默认是 x64-windows,如果你需要静态链接,可以指定x64-windows-static。安装完成后需要告诉 CMake 这个工具链文件:
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=[vcpkg根目录]/scripts/buildsystems/vcpkg.cmake如果你拿到的是预编译的.zip,里面的目录结构一般是include/和lib/。这时要手动在 CMakeLists.txt 里指定路径:
list(APPEND CMAKE_PREFIX_PATH "D:/aws-sdk-cpp")只要压缩包的目录结构里带了cmake/配置目录,find_package就能正确找到。另外,Windows 上特别容易踩的一个坑是动态库和静态库的选择。用预编译包时,一定要看它内置的AWS_SDK_LINKED_AS_SHARED_LIBRARY标记,如果你在 CMake 里定义的宏和预编译包不一致,链接会报一堆无法解析的外部符号。
3.3 在 CMake 工程中链接 SDK 的正确姿势
工程里配置 AWS SDK 链接,最直观的方式是使用官方的 CMake 配置文件。在 CMakeLists.txt 里这么写:
find_package(aws-cpp-sdk-s3 REQUIRED) find_package(aws-cpp-sdk-core REQUIRED) add_executable(my_aws_app main.cpp) target_link_libraries(my_aws_app PRIVATE aws-cpp-sdk-s3 aws-cpp-sdk-core) target_compile_features(my_aws_app PRIVATE cxx_std_11)代码里直接#include <aws/s3/S3Client.h>即可。这里有个小细节:如果 SDK 是动态编译的,在 Windows 上需要定义AWS_S3_EXPORTS之类的宏吗?不用,那是编译 SDK 时用的,使用方只需要链接正确的库即可。但如果是 Linux 上自己编译的静态库,需要额外链接平台相关依赖,比如pthread和curl,CMake 里可以这样补全:
find_package(CURL REQUIRED) target_link_libraries(my_aws_app PRIVATE ${CURL_LIBRARIES}) target_link_libraries(my_aws_app PRIVATE Threads::Threads)如果嫌麻烦,也可以直接用pkg-config来处理依赖。不过我用下来还是 CMake 的find_package最简单直接。
4. 第一个 C++ AWS 程序:S3 上传下载实战
4.1 初始化与凭据配置
S3 上传下载是 AWS SDK 最典型的入门场景。先放一个完整的可运行代码框架:
#include <aws/core/Aws.h> #include <aws/s3/S3Client.h> #include <aws/s3/model/PutObjectRequest.h> #include <aws/s3/model/GetObjectRequest.h> #include <iostream> #include <fstream> int main() { Aws::SDKOptions options; Aws::InitAPI(options); { Aws::S3::S3Client client; // 业务代码编写位置 } Aws::ShutdownAPI(options); return 0; }Aws::InitAPI和Aws::ShutdownAPI是必须成对出现的,建议放在main函数最外层。这段代码背后做的事情很多:初始化全局 HTTP 客户端、加载本地凭据、设置日志系统。如果你项目里用了多个线程同时调用 SDK,所有线程必须在这个作用域之内运行。脱离InitAPI作用域的线程只要调用了 SDK 接口,大概率会出现未定义行为。
凭据配置方面,SDK 按照固定的优先级查找:
- 环境变量
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY - 本地
~/.aws/credentials文件 - IAM 角色挂载(仅限 AWS 实例上运行时)
在本地开发时最常用的是前两种。S3Client默认区域是us-east-1,如果你的 bucket 在其他区域,必须显式指定,否则会报PermanentRedirect错误:
Aws::Client::ClientConfiguration config; config.region = "cn-north-1"; Aws::S3::S3Client client(config);4.2 上传文件与错误处理
写一个简单的上传本地文件到 S3 bucket 的函数:
bool PutFile(const std::string& bucket, const std::string& key, const std::string& file_path) { Aws::S3::S3Client client; Aws::S3::Model::PutObjectRequest request; request.SetBucket(bucket); request.SetKey(key); auto input_data = Aws::MakeShared<Aws::FStream>("PutObject", file_path.c_str(), std::ios::binary | std::ios::in); if (!input_data->good()) { std::cerr << "Failed to open file: " << file_path << std::endl; return false; } request.SetBody(input_data); auto outcome = client.PutObject(request); if (outcome.IsSuccess()) { return true; } std::cerr << "PutObject error: " << outcome.GetError().GetExceptionName() << ": " << outcome.GetError().GetMessage() << std::endl; return false; }关于内存管理,这里特意用了Aws::FStream而不是标准库std::ifstream,因为 SDK 内部要求 body 对象的类型必须满足它自己的内存分配规范。其实用std::make_shared<std::ifstream>也能编译通过,但可能出现运行时行为不一致的问题,我的经验是 SDK 例子里怎么写就怎么来,别搞原创。
4.3 异步接口的使用时机
SDK 的异步接口是用回调方式实现的,典型的下载场景如下:
void GetObjectAsync(const Aws::S3::S3Client& client, const std::string& bucket, const std::string& key) { Aws::S3::Model::GetObjectRequest request; request.SetBucket(bucket); request.SetKey(key); auto callback = [](const Aws::S3::S3Client*, const Aws::S3::Model::GetObjectRequest&, const Aws::S3::Model::GetObjectOutcome& outcome, const std::shared_ptr<const Aws::Client::AsyncCallerContext>&) { if (outcome.IsSuccess()) { auto& result = outcome.GetResult(); std::cout << "Download size: " << result.GetBody().rdbuf() << std::endl; } else { std::cerr << "GET object error: " << outcome.GetError().GetMessage() << std::endl; } }; client.GetObjectAsync(request, callback); }请注意,异步接口的回调是在 SDK 内部线程池上调用的,所以不要在回调里去操作已经销毁的变量。如果回调里涉及界面刷新或共享资源,务必加锁,或者把数据发到自己的消息队列里。这一点和任何 C++ 异步框架的习惯一样。
5. 线上踩坑与性能调优经验
5.1 常见编译/链接报错速查表
我把这段时间维护 C++ 服务遇到的典型问题整理成了速查表:
| 报错现象 | 根本原因 | 解决方案 |
|---|---|---|
fatal error: aws/core/Aws.h: No such file | 头文件路径未配置 | 检查target_include_directories或 SDK 安装路径是否正确 |
undefined reference to Aws::S3::S3Client::... | 链接库缺失或顺序错误 | 在target_link_libraries中把 SDK 库放到被依赖库前面 |
大量unresolved external symbol | 动态/静态链接方式不匹配 | 检查AWS_SDK_LINKED_AS_SHARED_LIBRARY宏是否与链接库类型一致 |
PermanentRedirect | 客户端区域和 bucket 区域不一致 | 初始化S3Client时显式设置config.region |
| 证书校验失败 | 本地 CA 证书缺失或过期 | 更新系统的 ca-certificates 包 |
链接顺序问题在 Linux 上特别妖,如果aws-cpp-sdk-s3依赖aws-cpp-sdk-core,但链接命令里写了aws-cpp-sdk-core在前、aws-cpp-sdk-s3在后,就会出现一堆 undefined reference。我一般是把 SDK 库列表放在所有自定义库之前,让链接器从右往左解析时能找到它们。
5.2 运行时问题:证书、超时与内存清理
运行时最常见的坑就是证书问题。如果你在 Linux 服务器上遇到curlCode: 77之类的报错,基本都是 CA 校验失败。最简单的解决方法是:
sudo apt install ca-certificates sudo update-ca-certificates如果是在内网访问自建 S3 兼容服务,可以通过ClientConfiguration里的verifySSL开关来规避证书问题,但生产环境不建议关掉校验。
内存清理也是容易出问题的点。SDK 内部大量使用shared_ptr,如果你把Aws::FStream对象赋给请求体后没保留引用,可能在请求完成前被释放。之前我遇到过一次“上传文件偶尔损坏”的诡异问题,最后排查下来就是 stream 对象被提前析构。另外,如果你的程序会长时间运行,记得定期调用Aws::ShutdownAPI和重新InitAPI?不,整个进程生命周期内只需要一次。SDK 的 HTTP 连接池会维持连接,这个和 curl 的全局状态类似,不要反复开关。
5.3 性能与体积优化心得
实际生产环境下,大部分人不会只上传一个小文件。大文件上传要优先使用 S3 Multipart Upload,SDK 的UploadPartRequest可以支持分片并行上传。我在一个项目里用 8 个并发分片上传 5GB 的大文件,速度比单请求快了三倍多。当然,分片大小和并发数的选择要结合带宽和内存来调整,分片过小会导致请求数量暴涨,网络往返成本反而上升。
另外是二进制体积问题。静态链接 AWS SDK 后,程序体积动辄增加几十 MB。如果项目对二进制体积有要求,可以尝试三种优化思路:
- 使用
BUILD_ONLY裁剪模块,不用的服务不编译进来。 - 编译时开启
-ffunction-sections -fdata-sections,链接时启用--gc-sections,把未使用的函数和数据回收掉。 - 如果真机上有对应动态库,直接动态链接是最优解,但要维护库的分发部署。
结尾
写了这么多,最后分享一点个人的实际体会。AWS SDK for C++ 的官方文档更像是一本字典,适合查阅,不太适合当教程从头读到尾。我快速上手的方式还是找一个开源项目,直接看它的 CMakeLists.txt 和核心调用代码,然后照着结构改。这个 SDK 的学习曲线主要不在 API 本身,而在 C++ 工程化那一整套东西——CMake 配置、依赖管理、链接策略、构建产物组织。只要把这几条理顺了,后面换任何 AWS 服务模块,写代码都只是照葫芦画瓢。
最后再提一个容易被忽略的小细节:如果你是在国内使用 AWS 服务,记得把ClientConfiguration里的endpointOverride配置好,某些情况下用官方默认 endpoint 会走绕路线路,延迟高得离谱。这不是 SDK 的问题,但属于实战里一定会遇到的优化点。希望这些经验能帮你少走几个弯路。
本文还有配套的精品资源,点击获取