最近公司要求做一个小工具,要求是通过程序连接 Linux 服务器,对服务器传送文件和执行命令。多次搜索后找到了比较适合的 libssh2 开源项目。但因为缺乏文档,一直没有编译成功。但最终经过多方搜索,还是成功在 VS2015 中将项目编译成功。特此写下详细步骤,希望能帮助各位。

下载安装 OpenSSL

要编译 libssh2,必须先编译好 OpenSSL 的静态库(我们要用静态库),幸运的是,我们不用自己编译(网上有很多自己编译的文章,问题多多、步骤繁琐且版本较老。),直接从 http://slproweb.com/products/Win32OpenSSL.html 下载已经编译好的包含 lib 和 include 文件的安装包即可。访问该网站点击下载完整的安装包,注意,不要下载 light 版本,因为 light 版本不带 lib 和 include。如下图: 2016-07-13_102013 下载完成后直接安装,我这里安装到 D:\OpenSSL-Win32 目录: 2016-07-13_104341 OpenSSL 就这样安装完毕了,如果你按网上自己编译的方法去自己编译,恐怕要耗上你至少半天的时间。我们来看看他的目录结构。 2016-07-13_104504

开始编译 libssh2

https://www.libssh2.org/snapshots/ 下载最新版本的源码包,解压出来后,用 VS2015 打开 libssh2-1.7.0\win32\libssh2.dsw(其实你可以用任何版本)。 2016-07-13_093950 此时会提示升级项目,点击确定升级即可: 2016-07-13_094116 升级完成后,项目成功加载,里面有两个工程,一个是 libssh2,一个是 tests。tests 是一个测试的项目,我们不用管,我们重点关注的就是 libssh2 项目。 打开 libssh2 的属性管理器,可以看到该项目被官方配置好了已经有很多编译方式: 2016-07-13_094232 因为我们要用 OpenSSL lib 的方式编译,且要 Release 版本的,所以我只用这个做演示,大家自己需要什么版本自己编译即可。首先双击 OpenSSL LIB Release Win32,编辑该项目属性,在 C/C++ -> 常规 -> 附加包含目录 中,添加 OpenSSL 的 include 路径 D:\OpenSSL-Win32\include,如下图: 2016-07-13_094333 接下来,添加 OpenSSL 的库路径和库文件到项目中。

  • 选择项目属性中的 库管理器 -> 常规 -> 附加库目录,添加 OpenSSL 的 Lib 库路径 D:\OpenSSL-Win32\lib\VC
  • 选择项目属性中的 库管理器 -> 常规 -> 附加依赖库,添加 OpenSSL 的 Lib 文件 libeay32MT.lib ssleay32MT.lib

这里大家可能注意到了,libeay32.lib 和 ssleay32.lib 分好多个版本,有 MD、MDd、MT、MTd 等,我们到底要选择哪个那?这要取决你编译这个库要使用的 运行库。选择该项目属性中的 C/C++ -> 代码生成 -> 运行库,根据你要调用 libssh2 的项目属性。 2016-07-13_094533 修改这里的属性,因为我要调用 libssh2 的项目使用的是 多线程MT,所以这里我也选择了 多线程MT,同时,你选择 libeay32.lib 和 ssleay32.lib 的时候,就根据这里的属性选择即可。 至此全部配置完毕了,编译一下项目,遇到了一个其他问题,编译器提示 snprintf 宏重定义2016-07-13_094917 这是因为在 libssh2_config.h 中重复定义了 snprintf 宏,只需要将 libssh2_config.h 中的定义注释即可: 2016-07-13_095252 当然,这不是最终解决方案,在 libssh2 的 github 中,有人曾提出了这个问题,并且目前 github 最新的代码已经解决了这个问题,其解决的办法是判断 VC 库的版本决定是否定义 snprintf 宏。参见:https://github.com/libssh2/libssh2/commit/ded55537acbfda9756667e054c22972842633cf4 注释掉冲突的代码后,再次重新编译项目,这次就可以编译通过了,后面有两条警告,无需理会,不影响我们使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1>------ 已启动生成: 项目: libssh2, 配置: OpenSSL LIB Release Win32 ------
1> agent.c
1> channel.c
1> comp.c
1> crypt.c
1> global.c
1> hostkey.c
1> keepalive.c
1> kex.c
1> knownhost.c
1> mac.c
1> misc.c
1> openssl.c
1> packet.c
1> pem.c
1> publickey.c
1> scp.c
1> session.c
1> sftp.c
1> transport.c
1> userauth.c
1> 正在生成代码...
1> 正在编译...
1> version.c
1> wincng.c
1> 正在生成代码...
1>ssleay32MT.lib(SSLEAY32.dll) : warning LNK4006: __NULL_IMPORT_DESCRIPTOR 已在 libeay32MT.lib(LIBEAY32.dll) 中定义;已忽略第二个定义
1>ssleay32MT.lib(SSLEAY32.dll) : warning LNK4221: 此对象文件未定义任何之前未定义的公共符号,因此任何耗用此库的链接操作都不会使用此文件
1> libssh2.vcxproj -> D:\libssh2-1.7.0\win32\.\Release_lib\libssh2.lib
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========

以下是我调试通过的 VS2015 的项目目录,用 VS2015 打开可以直接编译通过的,下载后打开 win32 目录,用 VS2015 打开 libssh2.sln 编译即可:点击下载 libssh2-1.7.0

测试 libssh2

接下来我们新建一个 Win32 控制台的空项目,新建一个 cpp 文件。 2016-07-13_132708 然后复制我们刚刚编译好的 libssh2 的库文件和所需的头文件。

  • 在新建的 Win32 项目目录下新建两个目录,一个叫 include,一个叫 lib
  • 复制我们刚才生成好的 libssh2-1.7.0\win32\Release_lib\libssh2.lib 到新建 Win32 项目的 lib 目录下。
  • 复制 libssh2-1.7.0\include 目录下所有文件到新建的 Win32 项目的 include 目录下。
  • 复制 libssh2-1.7.0\win32\libssh2_config.h 文件到新建的 Win32 项目的 include 目录下。

复制完成后的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
│  libssh2_example.vcxproj
│ libssh2_example.vcxproj.filters
│ main.cpp

├─include
│ libssh2.h
│ libssh2_config.h
│ libssh2_publickey.h
│ libssh2_sftp.h

└─lib
libssh2.lib

将测试项目的解决方案配置改为 Release,将 include 目录添加到项目的“附加包含目录”中。 2016-07-13_133857 将 lib 目录添加到项目的“附加库目录”中。 2016-07-13_133930 添加 libssh2.lib 到“附加依赖库”中。 2016-07-13_133955 这里项目属性就配置好了,我们到官网找一个测试代码,拷贝到我们的项目里试一下。打开 https://www.libssh2.org/examples,随便找一个例子,我这里找的是 ssh2_exec.c 的列子,选择 ssh2_exec.c 例子,将里面所有的代码复制出来到我们测试项目的 main.cpp 中,然后修改一下例子中连接服务器的 IP 地址、用户名口令等信息。 2016-07-13_135146 编译项目,提示了如下错误: 2016-07-13_134516 在代码的第一行(注意一定是第一行,所有 include 的前面)加上一句 #define _WINSOCK_DEPRECATED_NO_WARNINGS,禁用这个警告。 2016-07-13_134628 再次编译,提示找不到很多符号的错误: 2016-07-13_134715 这是因为我们使用了 Windows socket 库里面的函数,要包含一下 ws2_32.lib 库文件,一样在附加依赖库中,增加 ws2_32.lib2016-07-13_134926 再次编译,这次编译成功了。 2016-07-13_134959 CTRL+F5 运行一下测试项目,就可以看到执行命令的结果输出了。 2016-07-13_135131 至此,libssh2 的从编译到使用就全部记录完毕了,希望这篇文章能帮助在此碰壁的人。