ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows下VS2017编译libssh2:CMake与OpenSSL配置完整指南

Windows下VS2017编译libssh2:CMake与OpenSSL配置完整指南 简介VS2017下编译64位libssh2库往往让Windows开发者头疼源码依赖、CMake参数、OpenSSL路径都需逐一配置这套64位库文件资源包正是为此准备面向需要在Windows平台实现SSH2通信的C/C程序员适合远程管理工具开发、自动化部署模块、自定义SSH传输通道等典型场景。压缩包共115个文件以109个头文件为主附带3个.lib导入库、2个.dll动态库及1个cpp调用示例整体仅2.14MB目录结构清晰便于直接集成到工程中目前已有1507人学习下载。通过这套资源可快速将libssh2链接进项目示例代码清晰演示了SSH会话建立、认证与通道操作的基本流程配套的OpenSSL运行库一并提供从依赖层面排除了启动报错风险。头文件覆盖完整API声明导入库与动态库分别满足链接与运行时部署需求无论需要快速集成SSH能力还是对照文件结构理解libssh2的Windows构建产物都能获得实用参考。1. 为什么非要自己折腾编译libssh2做Windows C开发的朋友早晚会遇到需要SFTP、SCP或者SSH隧道功能的场景。我前阵子负责一个内部工具需要在Windows服务里定期把数据文件通过SFTP推送到远端服务器调研了一圈libssh2是最合适的选择它是纯C实现的SSH2协议库轻量、无运行时依赖、License友好功能覆盖了SFTP、SCP、公钥/密码认证这些主力需求。但在Windows平台上用libssh2第一个坑就是它官方发布的预编译二进制包几乎等于没有。GitHub Release页面通常只提供源码压缩包想拿到现成的.lib和.dll基本要靠第三方渠道或者自己编译。更麻烦的是我的项目要求64位而网上能找到的一些32位包、老版本包要么和我的VS版本不匹配要么OpenSSL依赖版本过旧要么干脆没法正常链接。折腾了一圈之后我决定与其到处找包、猜配置不如老老实实用VS2017自己编译一份64位的libssh2把依赖链握在自己手里。这篇博文就是把我这次完整编译过程、踩过的坑、排查过的报错全部记录下来。打算自己编译libssh2的朋友或者项目里需要定制品库比如要静态链接、要替换加密后端的开发者可以直接照着走能省下不少时间。2. 动手前的准备工作工具链与源码获取2.1 工具链清单VS2017、CMake、Perl、NASM一个都不能少在这件事上工具链的完整性直接决定了成功率。编译libssh2本身只需要VS2017和CMake但如果决定自己编译OpenSSL后文会讲为什么可能需要那就还需要Perl和NASM。我用的版本大致如下工具推荐版本用途Visual Studio 201715.9.x安装VC 2017工具集核心编译环境CMake3.15以上推荐3.20生成VS工程文件和编译驱动PerlStrawberry Perl 5.32编译OpenSSL必需NASM2.15编译OpenSSL的汇编优化代码必需如果你坚持只用CMake的默认配置不引入OpenSSLlibssh2也可以编译通过使用内置的mbedTLS或者纯内建加密后端。但我个人建议生产环境还是上OpenSSL它在算法支持、性能、兼容性方面都要稳得多后续对接其他需要SSL的库也方便。提示VS2017对应的是v141工具集。如果你机器上同时装了VS2019或VS2022编译时要特别注意选择v141否则生成的工程文件可能被新版工具集接管导致二进制接口不一致或者OpenSSL匹配问题。2.2 获取libssh2源码GitHub Release是最靠谱的渠道源码这块直接去libssh2的官方GitHub仓库找到最新的release版本下载压缩包即可。我这次用的是1.11.0版本release日期比较新修复了不少老问题对OpenSSL 1.1.1和3.x系列的兼容性都比较好。下载源码后解压到一个不含空格和中文的路径比如D:\build\libssh2-1.11.0。Windows下含空格的路径在CMake和编译器组合时大概率会出现引号转义问题这点务必注意。另外你还需要提前想清楚输出目录规划。我习惯建一个统一的第三方库目录比如D:\third_party下面再按库名和版本分目录存放这样后续多个项目引用时路径清晰不会乱。3. 核心依赖处理OpenSSL和Zlib3.1 OpenSSL的两种选择预编译包还是自己编译libssh2在Windows下最常用的加密后端是OpenSSL。OpenSSL的获取有两种路径第一种用现成的预编译包。网上有slproweb提供的Win64 OpenSSL安装包装完之后会自动设置环境变量把include、lib目录都配好。这种方式的优点是省事缺点是可定制性差而且它是自己编译的和你的VS版本、运行时可能不完全匹配。第二种从源码自己编译OpenSSL 1.1.1或3.x。这种方式可以精确控制输出格式、静态库还是动态库、是否带汇编优化等。我在实际项目中经常需要把OpenSSL也打成静态库和libssh2、我的业务代码一起静态链接最终产出一个完全无DLL依赖的exe所以这次我选择自己编。需要说明的是OpenSSL源码编译本身也是一套标准流程我在这里简述一下核心步骤展开讲可以单独写一篇了。# 在VS2017 x64 Native Tools Command Prompt中执行 perl Configure VC-WIN64A --prefixD:/third_party/openssl-1.1.1w nmake nmake install这里有几个关键点VC-WIN64A是OpenSSL针对Windows 64位和MSVC的编译目标--prefix指定安装目录头文件和库文件都会装到这里构建工具用nmake而不是CMake。如果没有Perl或NASMConfigure阶段就会报错这也是为什么我在工具清单里强调它们。3.2 Zlib可选但建议默认关闭libssh2支持通过Zlib做SSH包的压缩传输但这在生产环境里用处不大SSH通道的压缩收益在当今网络环境下非常有限反而增加CPU消耗。所以如果不需要压缩可以直接关掉Zlib依赖省去一大堆麻烦。我建议在CMake配置阶段把ENABLE_ZLIB_COMPRESSION设为OFF这样编译出来的库不需要Zlib。如果你的场景确实需要压缩那再单独编译一份64位的Zlib然后通过ZLIB_ROOT指向它即可。4. CMake配置与VS2017编译实战4.1 生成VS工程的核心参数libssh2的构建系统已经切到CMake这比老式的手写Makefile方便太多。打开Visual Studio 2017自带的“x64 Native Tools Command Prompt for VS2017”在源码根目录下执行如下命令mkdir build cd build cmake .. -G Visual Studio 15 2017 Win64 ^ -DCRYPTO_BACKENDOpenSSL ^ -DOPENSSL_ROOT_DIRD:/third_party/openssl-1.1.1w ^ -DOPENSSL_INCLUDE_DIRD:/third_party/openssl-1.1.1w/include ^ -DOPENSSL_SSL_LIBRARYD:/third_party/openssl-1.1.1w/lib/libssl.lib ^ -DOPENSSL_CRYPTO_LIBRARYD:/third_party/openssl-1.1.1w/lib/libcrypto.lib ^ -DBUILD_SHARED_LIBSOFF ^ -DBUILD_EXAMPLESOFF ^ -DBUILD_TESTINGOFF ^ -DENABLE_ZLIB_COMPRESSIONOFF这些参数每一个都有讲究。-G Visual Studio 15 2017 Win64指定生成64位VS2017工程这是整个命令里最关键的一项少写了Win64后缀生成的工程就是32位的CRYPTO_BACKENDOpenSSL明确告诉libssh2用OpenSSL作为加密后端后四个OpenSSL相关参数手动指定头文件和库文件路径这一步能避免CMake自动查找时找错版本。注意OPENSSL_ROOT_DIR、OPENSSL_INCLUDE_DIR、OPENSSL_SSL_LIBRARY、OPENSSL_CRYPTO_LIBRARY这四个变量在CMake的FindOpenSSL模块中各有分工。如果只设置OPENSSL_ROOT_DIRCMake通常也能找到其他三个但为了万无一失我建议全部显式指定。尤其是当系统里装了多个OpenSSL版本时依赖自动查找容易匹配到旧版本导致后续链接时符号冲突。4.2 正式编译一条命令搞定工程文件生成成功之后直接调用CMake的build指令编译。还是在上面的build目录下执行cmake --build . --config Release --target install--config Release指定编译Release版本--target install会在编译成功后把头文件、库文件、CMake配置文件复制到安装目录。默认安装路径是C:\Program Files\libssh2可以通过-DCMAKE_INSTALL_PREFIX来改比如cmake .. -DCMAKE_INSTALL_PREFIXD:/third_party/libssh2-1.11.0我实际编译过程中Release配置下大概两三分钟就完成了没有遇到报错。如果你在编译过程中看到类似“无法打开包含文件: openssl/evp.h”这样的错误90%的概率是OpenSSL的头文件路径没有正确传给编译器回到CMake配置阶段检查OPENSSL_INCLUDE_DIR即可。编译完的产物在build目录下的src\Release文件夹中静态库为libssh2.lib动态库为libssh2.dll和libssh2.lib。因为我们在配置阶段设置了BUILD_SHARED_LIBSOFF所以此时生成的应该是静态库。4.3 静态库和动态库这个选择不能拍脑袋BUILD_SHARED_LIBS这个选项很多人会随手选OFF或ON但这一步其实很考验项目的整体规划。选择静态库的好处是最终部署时不需要携带DLL程序拷到哪都能跑特别适合Windows服务、内部工具这类需要简化部署的场景。缺点是最终产物体积会增加而且需要把OpenSSL也编成静态库否则链接时会报错。如果选择动态库那么不光要带上libssh2.dll还要带上OpenSSL的DLL或者让OpenSSL也静态编进libssh2.dll这又是另一种玩法。好处是多个程序可以共享同一份DLL升级修复方便。我的建议是如果是内部工具、单程序分发选静态如果是平台级SDK、要提供给多个模块共用选动态。这没有绝对的对错想清楚自己的分发场景就行。5. 常见问题与排查技巧实录5.1 OPENSSL_ROOT_DIR死活找不到怎么办CMake配置阶段最常见的报错就是Could NOT find OpenSSL或者OPENSSL_ROOT_DIR设置了但没生效。排查路径依次是确认路径正确且目录下确实存在include/openssl/ssl.h和lib/libssl.lib。路径分隔符建议用正斜杠/避免反斜杠转义问题。在CMake配置时加上-DOPENSSL_USE_STATIC_LIBSTRUE这是因为我们编译的是OpenSSL静态库需要告诉CMake优先使用静态库而不是搜索DLL。如果机器上装了多个OpenSSL或者在非标准路径手动把四个变量全部显式传一次不要省。我曾经在排查这个问题时浪费了一个小时最后发现是路径末尾多了一个空格CMake把它当成路径的一部分。Windows下这种细微问题就是考研耐心。5.2 编译失败找不到openssl头文件这个问题跟在5.1里提到的报错类似但发生在编译阶段而不是配置阶段。典型报错是fatal error C1083: 无法打开包含文件: openssl/evp.h: No such file or directory原因基本就是OPENSSL_INCLUDE_DIR没有正确传给编译器。检查CMakeCache.txt里OPENSSL_INCLUDE_DIR的值如果为空或者路径错误重新跑一次CMake配置命令。注意CMakeCache.txt不会自动更新修改参数后最好删掉build目录重新配置。5.3 链接失败LNK1181无法打开libssh2.lib这个报错一般出现在你自己的项目链接阶段。原因不外乎两种一是没有在VS工程里正确配置附加依赖目录和附加依赖项二是你编译的是动态库但用了静态库的链接方式。我自己项目的VS工程配置如下一定要对着检查配置项值C/C → 附加包含目录D:/third_party/libssh2-1.11.0/include链接器 → 附加库目录D:/third_party/libssh2-1.11.0/lib链接器 → 附加依赖项libssh2.lib; libssl.lib; libcrypto.lib; ws2_32.lib; crypt32.lib; user32.lib; advapi32.lib注意ws2_32.lib是Windows Socket库libssh2在Windows上依赖Winsock2这个库不加上链接阶段会报一大堆unresolved external symbol错误比如__imp_htons、__imp_getaddrinfo之类的。crypt32.lib和advapi32.lib则是OpenSSL在某些Windows API上的依赖链接时需要一起带上。5.4 运行时崩溃OPENSSL_free未定义、Debug/Release不匹配还有一类问题是动态库和静态库混用导致的。比如libssh2用的是静态OpenSSL而你自己的项目也引用了OpenSSL的DLL版本两边同时工作时容易在内存释放阶段报错典型的就是OPENSSL_free未定义。更隐蔽的问题是Debug/Release配置不匹配。我用VS2017编译的libssh2是Release版但客户自己的程序用Debug模式编译链接后运行时频繁崩溃。原因在于C运行时库不同/MD vs /MDd数据结构布局可能有细微差异。解决方式就是要么Debug和Release各编译一份库要么让客户统一用Release。5.5 快速验证库是否可用的测试代码编译完成后我习惯写一个最小测试程序验证库能不能正常发起SSH连接。下面这个例子只做一件事初始化libssh2、连接指定主机的22端口、检查服务端指纹信息确认握手通了就算成功。#include libssh2.h #include winsock2.h #include ws2tcpip.h #include iostream #pragma comment(lib, ws2_32.lib) #pragma comment(lib, libssh2.lib) #pragma comment(lib, libssl.lib) #pragma comment(lib, libcrypto.lib) int main() { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), wsaData); libssh2_init(0); SOCKET sock socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(22); inet_pton(AF_INET, 192.168.1.100, addr.sin_addr); if (connect(sock, (sockaddr*)addr, sizeof(addr)) 0) { std::cerr connect failed std::endl; return -1; } LIBSSH2_SESSION* session libssh2_session_init(); if (libssh2_session_handshake(session, sock) ! 0) { std::cerr handshake failed std::endl; return -1; } std::cout SSH handshake OK! std::endl; libssh2_session_disconnect(session, bye); libssh2_session_free(session); closesocket(sock); libssh2_exit(); WSACleanup(); return 0; }这段代码能编译通过基本说明库本身是健康的。之前有一次我怀疑自己的libssh2编译有问题结果用这个测试程序跑了一遍才发现是客户环境的防火墙拦了22端口白熬夜调了半天。6. 写在最后几个编译之外的实用建议这次编译libssh2的整个过程前前后后大概花了我小半天时间其中有半天是在OpenSSL路径配置上栽跟头。如果让我重新来一次我会直接用vcpkg来跑一遍vcpkg install libssh2:x64-windows省心省力版本管理也方便。但如果你需要定制化比如静态链接、指定OpenSSL版本、裁剪掉不需要的模块那么手动编译的技能就不可替代了。这也是为什么我建议有条件的朋友还是亲手走一遍流程只有经历过配置、编译、链接、运行全链路的问题排查才能真正理解这个库的依赖关系后面遇到问题才能快速定位。最后分享一个很多教程不会提的小技巧编译完的libssh2最好把CMake的配置文件也保留一份。libssh2的CMake包支持通过find_package(libssh2)的方式集成到其他项目配置好CMAKE_PREFIX_PATH之后其他CMake项目可以直接引用不用再手动填头文件和库路径。这个细节在你后续维护多个项目时会特别有用。说到底编译第三方库这件事本身就是一场和环境的对话VS版本、OpenSSL版本、运行时配置、路径设置任何一个环节出了偏差都会导致最终结果不符合预期。耐心排查、逐个击破这个过程虽然折腾但走完一遍之后你对整个依赖链的掌控感会提升一大截。本文还有配套的精品资源点击获取
返回列表