简介:面向需要在 Windows 64 位环境下使用 SSH2 功能的 C/C++ 开发者,这份资源提供了基于 Visual Studio 2017 编译生成的 libssh2 库文件及配套头文件。资源包含 115 个文件,其中 109 个头文件覆盖 libssh2、OpenSSL 等 API 声明,3 个 .lib 导入库用于链接,2 个 .dll 运行时动态库,以及 1 个示例 cpp 文件,压缩包总大小 2.14MB。目前已有 1507 人学习下载。库文件对应 Release 配置,集成了 OpenSSL 1.1 依赖,可直接在 VS2017 工程中配置头文件目录与链接器输入,免去自行用 CMake 编译的繁琐步骤。对于希望快速接入 SFTP、远程 Shell 等 SSH 功能,又不想深究编译细节的开发者,是一份实用的现成组件。 有人看到“libssh2”这个库名,第一反应是去网上找个编译好的64位dll拿来用。我劝你趁早打消这个念头。且不说第三方预编译包更新时间滞后、编译选项成谜,单是“它到底绑了哪个加密后端”这一条,就够你在集成时踩一晚上的坑。真正省心的做法是自己拿VS2017编译一份64位的libssh2,过程不复杂,坑虽然有几个,但都是能绕过去的。
这篇东西适合在Windows上用C/C++做SFTP、SCP、SSH相关功能的开发者。我会把从环境准备、CMake生成工程、VS编译到集成使用的全流程讲清楚,中间会重点说几个文档里不会告诉你的细节。
1. 编译前先想清楚:选哪个后端
libssh2本身不实现加密算法,它需要依赖一个加密库来提供底层能力。编译前必须做这个选择题,因为后续所有步骤都跟它有关。
1.1 可选的三个后端
libssh2支持的加密后端有OpenSSL、WinCNG、Libgcrypt三个。在Windows平台上实际用得多的就前两个:
| 后端 | 依赖 | 优点 | 缺点 |
|---|---|---|---|
| OpenSSL | 需要单独准备OpenSSL库 | 功能最全、跨平台一致性好 | 需要额外编译或引入OpenSSL,配置路径麻烦 |
| WinCNG | 无需额外库,系统自带 | 零外部依赖、编译省事 | 某些算法套件受Windows版本影响 |
| Libgcrypt | 需要准备gcrypt库 | 较少用 | Windows下配置相对冷门 |
单从“自己编译libssh2”这件事来说,我建议直接用WinCNG后端。别纠结,WinCNG是Windows自带的加密API封装,libssh2对它的支持已经很成熟了。日常SFTP、SCP、SSH操作完全够用,而且少了OpenSSL这个“外部依赖”后,整个编译过程会清爽很多。
1.2 如果你坚持用OpenSSL后端
有的项目可能因为业务需要——比如要跟服务器端协商某个特定算法——必须用OpenSSL。这种情况下你需要先准备好64位的OpenSSL库。
最省事的方式是用vcpkg装:
vcpkg install openssl:x64-windows装好之后记下OpenSSL的安装路径,后续CMake配置时要用。不过这条路会多出不少变量,对新手不友好。我的建议很直接:第一遍编译先用WinCNG跑通全流程,后续有需求再切OpenSSL,至少你知道坑在哪一层。
2. CMake工程生成:别跳过版本检查
libssh2从很早的版本就开始支持CMake构建了,这一点对Windows用户来说是大福音。你不需要自己去写工程文件,CMake会自动帮你生成VS2017能直接打开的.sln。
2.1 确认你的CMake版本
VS2017对应的是Visual Studio 15系列,需要CMake 3.8及以上版本才能正确识别“Visual Studio 15 2017 Win64”这个生成器。如果你机器上的CMake版本过旧,生成时大概率会报错或者找不到VS2017。
在命令行里先确认版本:
cmake --version如果版本低于3.8,建议先升级CMake。官方下载页直接拿最新版即可,不用纠结。
2.2 下载libssh2源码
去GitHub的libssh2/libssh2仓库拉代码,或者直接下载release版源码包。版本号建议选较新的稳定版,比如1.10.0或更高。老版本不是不能用,只是新版本修了不少bug,尤其在WinCNG后端上有一些细节修复,没必要跟自己的时间过不去。
源码下载后解压到一个路径中,注意路径里尽量不要有中文和空格,否则后续CMake生成或VS编译时可能出现诡异的错误。
2.3 执行CMake生成
打开“适用于VS2017的开发者命令提示符”,或者直接在普通命令行里执行(前提是你把CMake加到了系统PATH中)。
到源码根目录创建一个build目录,然后执行:
mkdir build_x64 cd build_x64 cmake .. -G "Visual Studio 15 2017 Win64" -DCRYPTO_BACKEND=WinCNG说一下这段命令里几个关键参数的含义:
-G "Visual Studio 15 2017 Win64":指定生成器为VS2017,注意Win64后缀不能丢。你写“Visual Studio 15 2017”不带Win64的话,生成的是32位工程,后面编译出来还是32位库,等于白干。-DCRYPTO_BACKEND=WinCNG:指定加密后端为WinCNG。如果这个参数不写,CMake会尝试自动检测,而自动检测的逻辑在某些系统上会优先去找OpenSSL,找不到才切WinCNG——为了结果可控,建议显式指定。
CMake执行完成后,build_x64目录下会出现libssh2.sln,这就说明工程生成成功了。
3. 用VS2017编译libssh2:一步到位的配置
工程生成好了,编译本身反倒是最不用动脑的步骤。但有几个细节值得说明。
3.1 确认目标平台是x64
用VS2017打开libssh2.sln后,第一件事是看工具栏上的“解决方案平台”是不是x64。如果显示的是Win32,一定要切换到x64。CMake命令里指定了Win64,理论上生成的默认平台就是x64,但保险起见还是检查一下。
这里多说一句:检查方法是在“生成”菜单中选择“配置管理器”,看“活动解决方案配置”和“活动解决方案平台”两栏。如果平台不是x64,直接在下拉框里选。不要只在工具栏切换,要确认配置管理器里每一项的“平台”列都是x64。
3.2 动态库还是静态库:怎么选
生成之前还有一件事要确定好:你要DLL还是静态库。
CMake默认生成动态库(DLL)。如果你在CMake配置时加了-DBUILD_SHARED_LIBS=OFF,则生成静态库。
两种方式各有使用场景:
| 类型 | 生成文件 | 集成复杂度 | 适用场景 |
|---|---|---|---|
| 动态库 | libssh2.dll + libssh2.lib(导入库) | 低,需要带上DLL | 多个程序共用、发布灵活 |
| 静态库 | libssh2.lib(静态库) | 中,需要处理宏定义 | 单个可执行文件分发、简单部署 |
如果你拿不准,就选动态库,省心。静态库在Windows上涉及一个很经典的坑:编译客户端代码时,需要在预处理宏里手动添加LIBSSH2_STATIC之类的宏,否则函数符号导出声明对不上。这个细节网上资料说得不全,后面我会专门讲。
3.3 实际编译操作
确认平台和运行库设置后,直接“生成解决方案”或者“重新生成解决方案”。
如果你需要Release版和Debug版都编译一份,建议先编Release,再切到Debug编一次。因为两种配置生成的文件会分开放,互不覆盖。编译产物默认在build_x64目录下的src/Release和src/Debug,文件名会包含libssh2.dll和libssh2.lib(静态库模式)。如果你的工程配置不一样,也可以搜索整个build_x64目录,看到输出文件的位置。
3.4 完整集成清单:头文件与库文件
编译完了,怎么集成到自己的项目里?我一般习惯建一个third_party/libssh2目录,按下面的结构放:
third_party/libssh2/ ├── include/ │ ├── libssh2.h │ ├── libssh2_publickey.h │ ├── libssh2_sftp.h │ └── libssh2_config.h └── lib/ ├── x64/ │ ├── libssh2.dll │ ├── libssh2.lib这个libssh2_config.h很容易被忽略。它是CMake在build目录里自动生成的,OpenSSL后端和WinCNG后端生成的配置内容会不同。你编译自己的程序时,必须让编译器能找到它,否则libssh2.h里很多条件编译的宏是缺的。
VS2017中的配置方法是:
- C/C++ -> 常规 -> 附加包含目录:加上
include和include/libssh2_config所在的目录。 - 链接器 -> 常规 -> 附加库目录:加上
lib/x64。 - 链接器 -> 输入 -> 附加依赖项:写上
libssh2.lib。
4. 编译期间一定会碰到的几个坑
编译这块儿我前前后后帮人排查了不少回,有些坑属于“只要你不主动踩,它就一直等在那里”的等级。挑几个最典型的统一说说。
4.1 运行库不一致导致的链接错误
这是Windows下编C/C++库最大的坑,没有之一。libssh2默认CMake配置是按照你选的VS配置来决定运行库的,比如Debug模式默认是/MDd,Release默认是/MD。如果你的主程序用的是/MT(静态链接运行库),那么链接libssh2时就会遇到大量LNK2038或_ITERATOR_DEBUG_LEVEL不匹配的错误。
解决办法就是保持两边一致。要么把libssh2的CMake配置改掉,加参数-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL(在较新版本CMake中)/ 或者在VS工程的“代码生成->运行库”里手动调整匹配值。
这个坑在集成阶段才会暴露,排查起来非常费时间。建议在编译libssh2的时候就提前想好主程序用哪种运行库,而不是等到链接时才来追。
4.2 编译出来总感觉是32位的
很多人编译完,用sprintf或检查工具看dll,发现是x86的。原因多半是前面说的:CMake生成工程时没带Win64后缀,或者VS工程打开后解决方案平台被悄悄切回了Win32。
再提供一个更直接的确认方法:编译完成后,用VS自带的dumpbin或者随便一个PE信息查看工具,看目标文件的机器类型是不是x64:
dumpbin /headers libssh2.dll | findstr machine输出里出现x64就是对的。如果出现x86或14C之类的,赶紧检查上面的步骤。
4.3 静态库模式下链接报一堆“无法解析的外部符号”
这个问题很经典。Debug模式下尤其常见,而且报错信息一片一片的,完全不像是单个宏导致的。真实原因基本只有一个:你没有定义LIBSSH2_STATIC宏。
libssh2的头文件里是这么设计的:如果不定义这个宏,头文件里的函数声明会被修饰为__declspec(dllimport),这是导入动态库时用的。但在静态库模式下,根本没有对应的DLL,链接器自然找不到函数实现,只能报“无法解析的外部符号”。
解决方式:在项目的预处理定义中添加LIBSSH2_STATIC,然后重新编译。如果你用WinCNG后端,某些版本可能还需要LIBSSH2_WINCNG这个宏,这个在集成时也顺手加上,不会有副作用。
4.4 WinCNG后端首次连接出现“算法协商失败”
这不是编译问题,是运行期问题。如果你的服务器SSH配置比较老,只支持某些特定算法,而WinCNG后端的libssh2在某些Windows版本上默认不支持,就会出现连接断开、协商失败。解决方式基本是升级libssh2版本,或者让服务器端的加密算法套件往前兼容一点。
这种问题属于运行时错误,编译期完全暴露不出来,只能在实际连接时发现,所以排查时要心里有数。
5. 验证库是否可用的一个快速办法
编译完成、集成完CMake工程后,别急着写自己的业务代码。可以先写一个非常小的验证程序,只做两件事:初始化libssh2、检查版本号。
#include <stdio.h> #include <libssh2.h> int main() { libssh2_init(0); const char* version = libssh2_version(0); printf("libssh2 version: %s\n", version ? version : "unknown"); libssh2_exit(); return 0; }如果这段代码能编译、链接、运行并且打印出版本号,说明整个链路是通的,头文件、库文件、DLL三个环节都没问题。
另外一种更实际的验证是连接本机的ssh服务:
#include <stdio.h> #include <libssh2.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") int main() { WSADATA ws; WSAStartup(MAKEWORD(2, 2), &ws); libssh2_init(0); SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr = { 0 }; addr.sin_family = AF_INET; addr.sin_port = htons(22); inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); connect(sock, (sockaddr*)&addr, sizeof(addr)); LIBSSH2_SESSION* session = libssh2_session_init(); int rc = libssh2_session_handshake(session, sock); if (rc == 0) printf("SSH handshake succeeded\n"); else printf("SSH handshake failed: %d\n", rc); libssh2_session_free(session); closesocket(sock); libssh2_exit(); WSACleanup(); return 0; }这样就验证了加密后端、传输协议和socket层的全链路,比单纯查版本号更有说服力。
6. 最后的实操体会
编译libssh2本身真的不难,难点都集中在集成阶段——运行库冲突、宏定义缺失、32位/64位混淆,还有配置文件丢失。我建议你在项目里建一个名为docs/BUILD_NOTES.md的文档,把这次编译用的CMake命令、VS版本、编译配置、产物目录结构全部写进去。不要觉得这是多此一举,半年前编译出来的库,半年后再来维护时,你大概率会忘了当初是怎么编的。文档救命的场景我见过太多次了。
如果你还准备扩展SDK,那么建议把libssh2、OpenSSL这类第三方库统一放到一个独立目录,用脚本把include、lib、dll的拷贝流程固定下来。这样以后无论是升级libssh2版本还是移植到其他开发机,十分钟就能拉一套全新编译环境。编译库这种事,一次痛苦,后面全是收益。
本文还有配套的精品资源,点击获取