简介:在Windows 64位环境下集成SSH2能力时,C/C++开发者常被libssh2的编译流程困扰。这份资源直接给出使用VS2017编译完成的64位libssh2库,包内共115个文件,含109个头文件、3个静态库、2个运行所需DLL及1个调用示例源文件,覆盖SSH2核心API、OpenSSL加密依赖与基本使用演示,整体仅2.14MB,非常轻量。库已针对Release模式构建,拿到后只需在VS2017中配置头文件目录与链接器输入即可接入项目,省去手动配置CMake、OpenSSL及依赖环境的步骤。资源同时附带了libcrypto和libssl动态库,能有效减少OpenSSL版本不匹配问题,便于快速验证与二次开发。目前已有1507人学习下载,适合需要快速落地libssh2功能的中初级C/C++开发者参考使用。
1. 为什么要自己编译libssh2,而不是直接下载现成包
先说个故事。我去年接手一个Windows平台的内部工具项目,需要在C++代码里通过SFTP上传文件到远程服务器,第一个想到的方案就是libssh2——它是用C写的SSH2协议库,轻量、跨平台、无运行时依赖,在嵌入式领域和桌面工具里都有大量应用。它的定位和libcurl不同,libcurl是通用网络传输库,libssh2只聚焦SSH2协议本身,但正因为专注,它更纯粹,集成起来也更可控。
当时我的开发环境是VS2017,目标平台是x64。按理说,libssh2在GitHub上发布了预编译的二进制包,直接下载用不就行了?我一开始也这么想,但实际用起来发现几个问题:
- 官方提供的预编译包版本往往滞后,最新特性或者某些bugfix拿不到。
- 我需要同时集成OpenSSL做加密后端,预编译包未必和我的OpenSSL版本匹配,导致符号冲突或者ABI不兼容。
- 我想按自己的需要裁剪掉一些模块(比如不用zlib压缩),预编译包做不到。
所以最稳妥的方案就是在本地用VS2017手动编译一套64位的libssh2。这个过程我在网上查过不少资料,但很多教程要么太老,要么步骤不够完整,实际操作中会遇到各种莫名其妙的问题。这篇文章把我整个编译过程、踩过的坑、每一步的原理都梳理一遍,希望帮你省掉这些时间。
适合谁来参考?如果你用的是VS2017或VS2015/2019,目标平台是64位,需要在自己的项目里集成libssh2做SFTP/SCP或者远程命令执行,那这篇内容基本能覆盖你需要的全部信息。
2. 编译前置条件:工具链、CMake、OpenSSL和Perl
2.1 VS2017的C++工具链必须装全
很多人的VS2017在安装的时候只勾选了“.NET桌面开发”或者“通用Windows平台开发”,C++相关的组件压根没装,或者没装全。编译libssh2是纯C代码,但CMake在检测编译器的过程中会检查C和C++两套工具链。
你需要在Visual Studio Installer里确认安装了以下组件:
- “使用C++的桌面开发”工作负载
- 单个组件里的“Windows 10 SDK”(版本不限,但要匹配你系统)
- “VC++ 2017版本15.9 v14.16最新v141工具集”(每个版本的命名可能略有差异)
注意:如果你系统里装了多个版本的VS,CMake在生成项目文件的时候可能会选错编译器版本。后面我会讲怎么固定让CMake使用指定的VS版本。
2.2 CMake版本不是越新越好,但要够用
libssh2官方建议的CMake最低版本是3.15左右。我用的是CMake 3.20.3。你可以在cmake.org下载Windows安装包,安装时记得勾选“Add CMake to the system PATH for all users”,这样命令行里能直接敲cmake命令。
如果你不想装到系统里,也可以只用CMake的GUI图形界面,两个方式我都试过,各有优劣。这篇以命令行方式为主,因为它的可复现性更强——你写完一条命令,以后换台机器能一模一样地执行。
2.3 OpenSSL依赖:静态库还是动态库
libssh2的实际加密能力(AES、RSA、SHA等)依赖OpenSSL。编译libssh2时不强制依赖OpenSSL,它可以只用内置的crypto backend,但功能会受限,很多场景下没法满足需求。
我建议直接集成OpenSSL 1.1.1系列。为什么不推荐OpenSSL 3.x?因为3.x授权协议改成了Apache License 2.0,功能上是兼容的,但在Windows上编译静态库时配置流程和依赖项有细微差别。对大多数项目来说,1.1.1足够稳定,毕竟是LTS版本,技术支持到2023年9月。如果你要用3.x,编译思路一样,只是AES-NI这类优化需要额外处理一下。
获取OpenSSL编译产物的方式有两种:
- 从slproweb.com下载Shining Light Productions的预编译Windows安装包。安装时它会提供完整的include头文件、lib库文件和dll文件。
- 用Perl自己从源码编译OpenSSL。
我建议用方式1——预编译包足够日常使用,能省下大量时间。安装时注意选Win64 OpenSSL v1.1.1,而不是Win32版本。它的安装目录默认在C:\OpenSSL-Win64,里面会有include和lib两个文件夹,lib文件夹里有libssl.lib和libcrypto.lib。
提示:安装OpenSSL预编译包时最后一步会问你要不要复制DLL到系统目录,建议选“不复制”。后面在项目里部署DLL时,放到exe同目录比放系统目录要干净得多,升级和卸载都方便。
2.4 Perl:编译OpenSSL源码才需要
如果你走“源码编译OpenSSL”这条路,环境里必须有Perl,推荐Strawberry Perl。这个工具是Windows上编译OpenSSL的前提条件,因为OpenSSL的Configure脚本是用Perl写的。
如果你直接用预编译OpenSSL包,那Perl可以不用装。我这次为了省事用的是预编译包,所以Perl这一步直接跳过了。
2.5 编译环境变量确认
在开始之前,打开一个“x64 Native Tools Command Prompt for VS2017”(开始菜单里Visual Studio 2017文件夹下能找到),做一个检查:
cl如果输出版本信息,说明C++编译环境正常。再执行:
cmake --version确认CMake能正常调用。同时执行:
where cmake确认它在PATH里。
3. 用CMake配置libssh2的完整流程
3.1 获取libssh2源码
从libssh2的GitHub仓库拉取源码。建议不要直接下载zip包,而是用git clone方式,这样之后你想切换分支或者更新版本都很方便:
git clone https://github.com/libssh2/libssh2.git cd libssh2注意,当前master分支的版本可能需要更新的CMake配置,如果你想要稳定版本,可以切换到某个固定tag。我当时用的是libssh2-1.10.0这个tag:
git checkout libssh2-1.10.01.10.0版本对VS2017和CMake的兼容性都很好,编译过程很顺利。
3.2 创建构建目录
libssh2源码目录下最好不要直接生成构建文件,因为这样会污染源码树,以后更新代码的时候很麻烦。规范做法是创建一个独立的build目录:
mkdir build-vs2017-x64 cd build-vs2017-x643.3 关键的CMake命令
接下来这条命令是整个编译流程的核心,我逐段解释一下:
cmake .. -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=C:/libssh2-install -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -DCRYPTO_BACKEND=OpenSSL -DOPENSSL_ROOT_DIR=C:/OpenSSL-Win64参数拆解:
-G "Visual Studio 15 2017 Win64":指定生成器为VS2017的64位版本。这里有个细节:如果用"Visual Studio 15 2017"不带Win64,默认生成的是32位工程,网上很多人在这里踩坑。VS2017对应的generator名称是Visual Studio 15 2017,注意15是VS2017的内部版本号。-DCMAKE_INSTALL_PREFIX=C:/libssh2-install:指定install的安装目录。编译完成后运行cmake --install,会把头文件、库文件、cmake配置文件统一放到这个目录,方便项目引用。-DBUILD_SHARED_LIBS=ON:构建动态库(DLL)。如果你想要静态库(.lib),设为OFF即可。这个根据自己的需求选。-DBUILD_EXAMPLES=OFF:不编译示例程序,节约时间。-DBUILD_TESTING=OFF:不生成测试目标。libssh2的测试用例依赖一些Python库和外部SSH服务,本地跑起来很麻烦,先关掉。-DCRYPTO_BACKEND=OpenSSL:指定加密后端为OpenSSL。-DOPENSSL_ROOT_DIR=C:/OpenSSL-Win64:告诉CMake哪里找OpenSSL的头文件和库。
执行完后,CMake会输出一堆检测信息,最后出现“Generating done”字样,说明配置成功,当前目录下会生成libssh2.sln解决方案文件。
3.3.1 关于BUILD_SHARED_LIBS:动态库vs静态库,到底该怎么选
这是很多人犹豫的点。我的建议是:如果你的项目本身是动态加载插件架构,或者你不想让OpenSSL的符号泄漏出去污染其他DLL,选静态库(BUILD_SHARED_LIBS=OFF)。如果你的项目有多个模块都要用libssh2,静态库会导致每个模块各有一份拷贝,内存浪费不说,调试的时候符号还会互相打架,这时候选动态库更好。
另外要注意,选静态库时,libssh2.lib和libcrypto.lib有顺序要求,链接的时候libssh2必须在前、OpenSSL库在后,否则会出现无法解析的外部符号。
3.4 编译Debug和Release版本有区别
CMake生成VS解决方案后,在Visual Studio里打开或者直接MSBuild编译都可以。我习惯直接用命令行编译:
cmake --build . --config Release --parallel 8--config Release是关键参数,因为VS多配置生成器默认会有Debug、Release、RelWithDebInfo、MinSizeRel四种配置,不指定的话有些目标会全部编译一遍,浪费时间。--parallel 8表示8线程并行编译。
编译完成后,在build-vs2017-x64/src/Release目录下应该能看到:
libssh2.dlllibssh2.lib
Debug版本的产物在src/Debug目录下,文件名里会带一个d后缀(libssh2d.dll)吗?实际上libssh2不会在文件名里加d,它只是输出路径不同。但implicit linking时使用的导入库名是一样的,所以Debug和Release工程的LIB文件名一样,需要你在自己的VS工程里注意选择对应目录的lib。
4. OpenSSL依赖处理的几种姿势
4.1 路径处理最容易出问题
我第一次配置的时候,把OPENSSL_ROOT_DIR指到了C:\OpenSSL-Win64,这个变量里包含了完整路径。但有个小坑:如果你的路径中间有空格(比如C:\Program Files\OpenSSL),CMake有时候解析会出问题,宏定义里会出现路径截断的奇怪现象。
最省心的做法是把OpenSSL放到一个没有空格的路径下,比如C:\OpenSSL-Win64。因为CMake在生成项目文件时,会把路径引用写到vcxproj文件的AdditionalIncludeDirectories和AdditionalLibraryDirectories里。有空格时会自动加引号,但个别老版本CMake处理得并不好,所以我们从源头避免它。
4.2 检查CMake是否找到OpenSSL
执行CMake配置时,注意输出里有没有这两行:
Found OpenSSL: C:/OpenSSL-Win64/lib/libcrypto.lib (found version "1.1.1w")如果出现Could NOT find OpenSSL,那就说明路径配错了。可以用这个命令查看CMake缓存里OpenSSL相关的变量:
cmake -LA . | findstr OPENSSL正常情况会输出:
OPENSSL_CRYPTO_LIBRARY:FILEPATH=C:/OpenSSL-Win64/lib/libcrypto.lib OPENSSL_INCLUDE_DIR:PATH=C:/OpenSSL-Win64/include OPENSSL_SSL_LIBRARY:FILEPATH=C:/OpenSSL-Win64/lib/libssl.lib如果这些变量还是NOTFOUND,多半是路径写错了或者32位/64位混用了。
4.3 为什么不建议同时设置OPENSSL_INCLUDE_DIR和OPENSSL_ROOT_DIR
这两个变量一个指定头文件目录,一个指定库目录。如果同时设置但指向了两个不同版本的OpenSSL目录,编译的时候头文件用的是A版本、链接时库用的B版本,轻则警告,重则运行时崩溃。我建议只设置OPENSSL_ROOT_DIR,让CMake的FindOpenSSL模块自己推断include和lib的位置。
5. 集成到自己的VS2017项目
5.1 在工程属性里配置头文件和库
编译好libssh2之后,在VS2017里新建一个自己的项目,然后在项目属性页里设置:
配置C/C++ -> 常规 -> 附加包含目录,添加:
C:\libssh2-install\includeC:\OpenSSL-Win64\include
配置链接器 -> 常规 -> 附加库目录,添加:
C:\libssh2-install\libC:\OpenSSL-Win64\lib
配置链接器 -> 输入 -> 附加依赖项,添加:
libssh2.lib
如果用的是静态库而不是动态库,还需要额外链接OpenSSL的库:
libssl.liblibcrypto.lib
5.2 运行时的DLL部署
动态库的方式编译时,运行你的exe之前需要把三个DLL放到exe同目录,或者放到系统PATH里:
libssh2.dlllibssl-1_1-x64.dlllibcrypto-1_1-x64.dll
我一般的习惯是写一个部署脚本,用xcopy把这三个DLL拷贝到输出目录:
xcopy /Y C:\libssh2-install\bin\libssh2.dll $(OutDir) xcopy /Y C:\OpenSSL-Win64\bin\libssl-1_1-x64.dll $(OutDir) xcopy /Y C:\OpenSSL-Win64\bin\libcrypto-1_1-x64.dll $(OutDir)放到预构建事件里,每次编译自动执行,省得手动去翻目录。
5.3 最小测试代码
配置完之后,写一个最简单的调用验证一下:
#include <libssh2.h> #include <iostream> int main() { libssh2_init(0); std::cout << "libssh2 version: " << LIBSSH2_VERSION << std::endl; libssh2_exit(); return 0; }如果编译链接通过,运行输出libssh2的版本号,说明整个编译链路已经通了。
6. 编译过程中可能遇到的坑及排查方法
6.1 错误:无法打开文件libssh2.lib
这个错误发生在链接阶段。基本原因只有一个——链接器找不到导入库。检查三点:
- 附加库目录路径是否正确,用绝对路径,别用相对路径。
- lib文件名是否匹配。有的教程编译出来是
libssh2.lib,但如果你设置了CMAKE_DEBUG_POSTFIX,Debug版本可能叫libssh2d.lib。 - 编译架构是否一致。你的项目平台是x64,链接的lib也必须是x64编译出来的。用32位的lib去链接x64工程,会出现LNK1112错误。
6.2 错误:LNK2038 不匹配
这是VS2017经常出现的经典错误:
LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MD_DynamicRelease”不匹配值“MT_StaticRelease”意思是说,你的主工程用了动态运行时(/MD),但libssh2是用静态运行时(/MT)编译的,两者不匹配。要解决需要在编译libssh2时保持和主工程一致的运行时库设置。
有两种改法:
- 改libssh2的CMake配置,在CMakeLists里设置
CMAKE_C_FLAGS_RELEASE为/MD。 - 或者更推荐的做法:让你主工程统一使用和libssh2一样的运行时。比如libssh2默认是/MD,那你的主工程就把“代码生成 -> 运行库”改为“多线程DLL(/MD)”。
这个不匹配的原因是MSVC会把运行时类型编码到lib的符号里,链接的时候做一致性检查。做跨库集成时这是最常见的问题,记得先检查运行库设置。
6.3 错误:无法解析的外部符号__imp_*相关
形如:
unresolved external symbol __imp_libssh2_session_init referenced in function main说明你在链接一个用__declspec(dllimport)声明的函数,但对应导入库没有被正确链接。一般是因为只加了include路径而忘了加lib目录,或者lib目录背景选的还是Debug模式但在Release下编译。还有一种可能是你链接的不是导入库而是静态库,但代码里LIBSSH2_API的声明被定义成了__declspec(dllimport)——这可以在预处理器里加LIBSSH2_API=来避免。
6.4 编译警告C4996
有时会看到:
warning C4996: 'strcpy': This function or variable may be unsafe.这是微软对非安全版本C库函数的提示,不是错误。可以在预处理定义加_CRT_SECURE_NO_WARNINGS来消除。因为libssh2源码原本是跨平台的,后端的加密逻辑在Windows上触发这类警告很正常,不影响使用。
6.5 运行时找不到DLL
编译链接都通过了,但运行时弹窗提示找不到libssh2.dll。
解决办法我之前已经说过,把三个DLL放exe同目录。不过我注意到一个现象:如果把DLL放到系统System32目录,有时候会加载到旧版本的同名DLL(比如其他软件塞进去的),导致接口不匹配崩溃。所以尽量坚持DLL和exe同目录原则,不要往系统目录放。
7. 关于静态编译和动态编译的实际选择建议
先说结论:可分发的小工具,强烈建议用静态编译。原因很简单——动态编译需要带三个DLL,不管做安装包还是绿色版,文件都会多出好几MB,而且OpenSSL的DLL在不同机器上可能出现各种兼容性问题。尤其是目标机器上如果装了其他用OpenSSL的软件,DLL替换升级时很可能崩掉。
静态编译要注意的就是上面提到的运行库一致性问题。把BUILD_SHARED_LIBS设为OFF,同时编译整个依赖链时保持所有库的运行库设置一致。另外静态编译的libssh2连接OpenSSL时,需要在链接参数里保证库的顺序:
libssh2.lib libssl.lib libcrypto.lib如果还依赖了zlib,加上zlib.lib。
如果编译顺序反了,你可能会遇到很多奇怪的“无法解析的外部符号”,其实不是真的缺符号,而是链接器按照从左到右的顺序解析库符号时,前面的库引用后面的库符号没法回填。MSVC不像GCC那样会循环解析。所以库顺序是静态链接时最容易忽视的细节。
7.1 静态库的调试符号问题
如果你选择了静态编译并准备在开发阶段调试libssh2内部的调用流程,记得在CMake配置时把CMAKE_BUILD_TYPE设为对应配置并打开调试信息。使用VS多配置生成器时,Debug配置默认会带上/Zi,这没问题。在你的主项目里,把附加依赖库切换为libssh2d.lib(如果设置了postfix)就能断点调试到libssh2源码内部。
8. 我编译过程中翻过的最大的车
最后分享一个我真实遇到的坑,希望你别再踩。
我当时手头有个老项目依赖了32位的第三方库,主工程一直是Win32平台。后来要出一个x64版本时,我直接把整个解决方案切到了x64,编译时发现libssh2链接怎么都过不去。排查半天发现,我的OPENSSL_ROOT_DIR还指向C:\OpenSSL-Win64没错,但这个目录装的是32位还是64位,我自己都没确认。用dumpbin /headers libcrypto.lib一查,文件头里写着machine (x86)。也就是说,我用32位的OpenSSL去配64位的libssh2,CMake竟然没报错,编译也过了,但链接时符号对齐全乱了。
所以建议你拿到OpenSSL预编译包后,先确认一下它的架构:
dumpbin /headers C:\OpenSSL-Win64\lib\libcrypto.lib | findstr machine输出x64才是对的。
还有一次,我换了一台新机器重新编译,CMake配置完成后编译报错,提示找不到win32相关头文件。原因是新机器上Windows SDK没装。VS2017在安装时默认会装最新版WDK或者SDK,但如果选了自定义安装,很容易漏掉。重新运行VS安装器补一个“Windows 10 SDK”组件就好。
9. 给Windows下习惯用命令行人的额外建议
在Windows环境下用CMake命令行编译,建议先把VS的环境变量导入当前控制台再执行。也就是打开“x64 Native Tools Command Prompt for VS2017”,而不是用普通的PowerShell。因为普通PowerShell里没有cl.exe的环境变量,CMake虽然能自己找到编译器,但有时候还是会因为找不到rc.exe(资源编译器)报错。
如果你实在只有PowerShell,可以用vcvars64.bat手动导入环境:
cmd /k "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"注意路径里的版本号和VS版本(Community/Professional/Enterprise)需要按你自己的安装情况改。
10. 最后的最后,一个小技巧
编译完成后,C:\libssh2-install目录里会有一个libssh2-config.cmake文件。下次你在自己的CMake工程里,可以直接用find_package(libssh2 REQUIRED)来引用它,不用手动添加include和lib路径:
set(libssh2_DIR "C:/libssh2-install/lib/cmake/libssh2") find_package(libssh2 REQUIRED) target_link_libraries(my_target PRIVATE libssh2::libssh2)这会帮你省掉一堆工程配置的琐碎步骤。如果你只在VS里做小工程,用我之前说的属性页手工配置也完全够用。
我在实际编译libssh2的过程中,第一次配置花了整整一个下午,大部分时间都耗在OpenSSL路径和运行库不匹配这类问题上。但搞定一次之后,后面换版本、换机器、换依赖项都变得很轻松,基本上十分钟内能跑通全流程。希望这篇文章能帮你把这个过程压缩到一杯咖啡的时间。
本文还有配套的精品资源,点击获取