参考链接:SFTP客户端代码示例
操作系统:Windows7/8,VS2013
环境:libssh2 1.4.3、zlib-1.2.8、openssl-1.0.1g
原文:
“从http://www.libssh2.org/下载libssh2-1.4.3.tar.gz文件,解压后打开libssh2.dsw文件升级项目到VisualStudio 2013,里面有两个项目,只要编译libssh2项目就可以了。编译前需要添加zlib和openssl的头文件和库文件链接位置,如果编译libssh2提示找不到msvcrt.lib,为链接库添加下面的路径
C:\Program Files (x86)\Microsoft VisualStudio 12.0\VC\lib
提示找不到ws2_32.lib或odbc32.lib,添加下面的链接路径
C:\Program Files (x86)\MicrosoftSDKs\Windows\v7.1A\Lib
编译通过后文件输出到\libssh2-1.4.3\win32\Release_lib路径下”
更新:
http://www.libssh2.org/网址访问不通,故换个方式下载了libssh2-1.4.3.tar.gz文件,下载地址:百度网盘(密码:2hwo)
libssh2.dsw文件位置:\libssh2-1.4.3\libssh2-1.4.3\win32
zlib的头文件是zlib.h,库文件是:zlib.lib
openssl的头文件是:opensslconf.h,库文件是:libeay32.lib,ssleay32.lib
zlib的头文件(zlib.h)位置:\libssh2-1.4.3\zte\zlib-1.2.5,库文件(zlib.lib)位置:\libssh2-1.4.3\zte\openssl-1.0.0b staticlib\Debug
openssl头文件(opensslconf.h)位置:\libssh2-1.4.3\zte\openssl-1.0.1l\include\openssl,库文件(libeay32.lib,ssleay32.lib)位置:\libssh2-1.4.3\zte\openssl-1.0.0b staticlib\Debug
分两个步骤:
1.先编译输出libssh2.lib
(1)配置好zlib和openssl的头文件和库文件链接位置后,编译libssh2项目即可,这时可以把tests项目卸载,反正用不到嘛;
编译成功后,输出libssh2.lib到\libssh2-1.4.3\win32\Release_lib路径下,(ps:我这里生成的是libssh2d.lib,我把文件名改为libssh2.lib后可以正常使用)
2.执行客户端,进行下载图片;
新建个项目,把下文三个文件拷贝到项目中,配置libssh2.h,libssh2.lib链接位置后,即可执行成功;
注意:路径及文件名不能含有中文,否则会报错;
下面是SFTP客户端示例代码:
三个文件的下载地址:百度网盘(密码:1qhf)
main.cpp
1 #include "SFTP_Libssh2.h" 2 #include <iostream> 3 4 int main(int argc, char* argv[]) 5 { 6 //下面的代码只要在进程初始化的时候执行 7 kagula::network::SFTP_Init(); 8 9 //测试SFTP链接 10 kagula::network::SFTP_Libssh2* client = kagula::network::SFTP_Libssh2::Inst(); 11 std::string ip = "192.168.19.130"; 12 uint16_t port = 22; 13 std::string usr = "kagula"; 14 std::string pwd = "123456"; 15 if (false == client->IsAbilityConn(ip, port, usr, pwd)) 16 { 17 std::cout << client->strLastError << std::endl; 18 return -1; 19 } 20 21 //测试文件上传,d:\\temp\\a.html 22 if (0 != client->upload(ip, 22, usr, pwd, "d:\\temp\\a.html", "/home/kagula/a.html")) 23 { 24 std::cout << "Error:" << client->strLastError << std::endl; 25 } else 26 { 27 std::cout << client->strLastError << std::endl; 28 } 29 30 31 //测试文件下载 32 if (0 != client->download(ip, 22, usr, pwd, "/home/kagula/a.html","d:\\temp\\b.html" )) 33 { 34 std::cout << "Error:" << client->strLastError << std::endl; 35 } 36 else 37 { 38 std::cout << client->strLastError << std::endl; 39 } 40 41 //进程准备结束,释放资源的时候,运行下面的代码 42 kagula::network::SFTP_Exit(); 43 return 0; 44 }
SFTP_Libssh2.h
1 #pragma once 2 3 #include <string> 4 #include <atomic> 5 6 /* 7 功能:SFTP协议的文件传输功能 8 最后更新日期:2014-5-17 9 简介:借助Libssh2库很容易实现sftp,ssh2客户端,这里给出 10 如何实现Sftp客户端的代码 11 测试环境:Windows 8.1 64bit、Visual Studio 2013 Professional SP1 12 OpenSSL 1.0.1g、zlib-1.2.8、libssh2 1.4.3 13 Win32控制台项目 14 注意:动态链接需要把“libssh2.dll”文件复制到当前项目路径下 15 说明:原来的代码支持多线程,从应用程序抽出来的时候简化了, 16 你可以修改代码使它同时支持上传或下载多个文件。 17 建议:[1]第三方库直接下载源代码自己编译免得库由于编译器版本的 18 不同或设置的不同链接的时候一大堆麻烦。 19 [2]读懂代码根据项目需求作相应修改 20 补充阅读资料: 21 《使用libssh2库实现支持密码参数的ssh2客户端》 22 http://blog.chinaunix.net/uid-24382173-id-229823.html 23 */ 24 namespace kagula { 25 namespace network { 26 int SFTP_Init(); 27 void SFTP_Exit(); 28 29 class SFTP_BKCall 30 { 31 public: 32 /* progress返回值范围[0.0,1.0] */ 33 virtual void OnProgress(float progress) = 0; 34 }; 35 36 class SFTP_Libssh2 37 { 38 public: 39 static SFTP_Libssh2* Inst() 40 { 41 static SFTP_Libssh2 inst; 42 43 return &inst; 44 } 45 46 /* 47 入口参数使用说明 48 ip: 就填一个IP地址就好了,例如“127.0.0.1”。 49 port: 端口,SFTP服务器默认端口为22。 50 username: 51 password: 52 sftppath: 远程路径“/”开头 ,例如“/a.jpg” 53 localpath: 本地路径,例如“d:\\temp\\test.jpg” 54 strLastError: 错误信息 55 出口参数 56 返回不等于零,代表失败! 57 */ 58 int upload(std::string ip, unsigned short port, std::string username, 59 std::string password, std::string localpath, std::string remotepath); 60 int download(std::string ip, unsigned short port, std::string username, 61 std::string password, std::string sftppath, std::string localpath); 62 63 //测试SFTP服务器是否可以链接 64 bool IsAbilityConn(std::string ip, unsigned short port, std::string username, 65 std::string password); 66 67 //设置回掉函数 68 void SetBKCall(SFTP_BKCall *bkCall) { m_bkCall = bkCall; } 69 70 //存放最近的错误信息 71 std::string strLastError; 72 73 //用于停止当前上传或下载线程 74 void stop() { m_isBreak.store(true); } 75 private: 76 SFTP_Libssh2() :m_bkCall(NULL) { m_isBreak.store(false); };//防止直接初始化 77 SFTP_Libssh2(const SFTP_Libssh2&); //防止拷贝复制 78 SFTP_Libssh2& operator=(const SFTP_Libssh2&); //防止分配(运算符函数的调用) 79 80 SFTP_BKCall *m_bkCall; 81 std::atomic_bool m_isBreak; //带读写保护的bool值 82 }; 83 } 84 }
SFTP_Libssh2.cpp
1 //SFTP_Libssh2.cpp文件清单 2 #include "SFTP_Libssh2.h" 3 4 #include <libssh2.h> 5 #include <libssh2_sftp.h> 6 7 #ifdef HAVE_WINSOCK2_H 8 #include <winsock2.h> 9 #endif 10 #ifdef HAVE_SYS_SOCKET_H 11 #include <sys/socket.h> 12 #endif 13 #ifdef HAVE_NETINET_IN_H 14 #include <netinet/in.h> 15 #endif 16 #ifdef HAVE_UNISTD_H 17 #include <unistd.h> 18 #endif 19 #ifdef HAVE_ARPA_INET_H 20 #include <arpa/inet.h> 21 #endif 22 #ifdef HAVE_SYS_TIME_H 23 #include <sys/time.h> 24 #endif 25 26 #include <sys/types.h> 27 #include <fcntl.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include <ctype.h> 31 32 #include <sstream> 33 #include <iomanip> 34 35 #pragma comment(lib, "ws2_32.lib") 36 37 #pragma comment(lib, "libeay32.lib") 38 #pragma comment(lib, "libssh2.lib") 39 40 namespace kagula { 41 namespace network 42 { 43 //初始化进程的时候调用 44 //如果非0表示初始化失败! 45 int SFTP_Init() 46 { 47 WSADATA wsadata; 48 int rc = WSAStartup(MAKEWORD(2, 0), &wsadata); 49 if (rc != 0) { 50 return rc; 51 } 52 53 rc = libssh2_init(0); 54 55 return rc; 56 } 57 58 //进程结束的时候调用 59 void SFTP_Exit() 60 { 61 libssh2_exit(); 62 63 WSACleanup(); 64 } 65 66 bool SFTP_Libssh2::IsAbilityConn(std::string ip, unsigned short port, std::string username, 67 std::string password) 68 { 69 unsigned long hostaddr; 70 struct sockaddr_in sin; 71 const char *fingerprint; 72 LIBSSH2_SESSION *session; 73 int rc; 74 bool bR = false; 75 FILE *local; 76 LIBSSH2_SFTP *sftp_session; 77 LIBSSH2_SFTP_HANDLE *sftp_handle; 78 79 hostaddr = inet_addr(ip.c_str());//hostaddr = htonl(0x7F000001); 80 81 82 //新建连接 83 int sock = socket(AF_INET, SOCK_STREAM, 0); 84 85 sin.sin_family = AF_INET; 86 sin.sin_port = htons(port); 87 sin.sin_addr.s_addr = hostaddr; 88 if (connect(sock, (struct sockaddr*)(&sin), 89 sizeof(struct sockaddr_in)) != 0) { 90 std::ostringstream ostr; 91 ostr << "[" << __FILE__ << "][" << __LINE__ << "]failed to connect" << ip << "!" << std::endl; 92 strLastError = ostr.str(); 93 94 return bR; 95 } 96 97 //新建对话实例 98 session = libssh2_session_init(); 99 if (!session) 100 { 101 closesocket(sock); 102 return bR; 103 } 104 105 //设置调用阻塞 106 libssh2_session_set_blocking(session, 1); 107 108 //进行握手 109 rc = libssh2_session_handshake(session, sock); 110 if (rc) { 111 std::ostringstream ostr; 112 ostr << "[" << __FILE__ << "][" << __LINE__ << "]Failure establishing SSH session: " << rc << std::endl; 113 strLastError = ostr.str(); 114 115 libssh2_session_free(session); closesocket(sock); 116 return bR; 117 } 118 119 //检查主机指纹 120 std::ostringstream ostr; 121 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 122 ostr << "Fingerprint: "; 123 for (int i = 0; i < 20; i++) { 124 unsigned char c = fingerprint[i]; 125 int nT = c; 126 ostr << std::hex << std::setw(2) << std::setfill('0') << nT; 127 } 128 strLastError = ostr.str(); 129 130 //通过密码验证登陆用户身份 131 if (libssh2_userauth_password(session, username.c_str(), password.c_str())) { 132 std::ostringstream ostr; 133 ostr << "[" << __FILE__ << "][" << __LINE__ << "]Authentication by password failed." << std::endl; 134 strLastError = ostr.str(); 135 goto shutdown; 136 } 137 138 sftp_session = libssh2_sftp_init(session); 139 140 if (!sftp_session) { 141 std::ostringstream ostr; 142 ostr << "[" << __FILE__ << "][" << __LINE__ << "]Unable to init SFTP session " << std::endl; 143 strLastError = ostr.str(); 144 145 goto shutdown; 146 } 147 148 bR = true; 149 150 151 libssh2_sftp_shutdown(sftp_session); 152 153 shutdown: 154 libssh2_session_disconnect(session, 155 "Normal Shutdown, Thank you for playing"); 156 libssh2_session_free(session); 157 closesocket(sock); 158 return bR; 159 } 160 161 /* 162 源码参考地址 163 http://www.libssh2.org/examples/sftp_write.html 164 */ 165 int SFTP_Libssh2::upload(std::string ip, unsigned short port, std::string username, std::string password, 166 std::string localpath, std::string remotepath) 167 { 168 if (ip.length()<1 || username.length()<1 || password.length()<1 || localpath.length()<1 || remotepath.length()<1) 169 { 170 return -1; 171 } 172 173 int nR = 0; 174 unsigned long hostaddr; 175 struct sockaddr_in sin; 176 const char *fingerprint; 177 LIBSSH2_SESSION *session; 178 int rc = -1; 179 FILE *local = NULL; 180 LIBSSH2_SFTP *sftp_session; 181 LIBSSH2_SFTP_HANDLE *sftp_handle; 182 183 hostaddr = inet_addr(ip.c_str());//hostaddr = htonl(0x7F000001); 184 185 if (fopen_s(&local, localpath.c_str(), "rb") != 0) { 186 std::ostringstream ostr; 187 ostr << "[" << __FILE__ << "][" << __LINE__ << "]Can't open local file " << localpath << std::endl; 188 strLastError = ostr.str(); 189 190 return -2; 191 } 192 193 //取待上传文件整个size. 194 fseek(local, 0, SEEK_END); 195 size_t filesize = ftell(local);//local file大小,在readFromDisk中被引用 196 fseek(local, 0, SEEK_SET);//文件指针重置到文件头 197 198 //新建连接 199 int sock = socket(AF_INET, SOCK_STREAM, 0); 200 201 sin.sin_family = AF_INET; 202 sin.sin_port = htons(port); 203 sin.sin_addr.s_addr = hostaddr; 204 if (connect(sock, (struct sockaddr*)(&sin), 205 sizeof(struct sockaddr_in)) != 0) { 206 std::ostringstream ostr; 207 ostr << "[" << __FILE__ << "][" << __LINE__ << "] failed to connect " << ip << std::endl; 208 strLastError = ostr.str(); 209 210 fclose(local); 211 return -3; 212 } 213 214 215 //创建会话实例 216 session = libssh2_session_init(); 217 if (!session) 218 { 219 fclose(local); closesocket(sock); 220 return -4; 221 } 222 223 //阻塞方式调用libssh2 224 libssh2_session_set_blocking(session, 1); 225 226 //进行握手 227 rc = libssh2_session_handshake(session, sock); 228 if (rc) { 229 std::ostringstream ostr; 230 ostr << "[" << __FILE__ << "][" << __LINE__ << "] Failure establishing SSH session:" << rc << std::endl; 231 strLastError = ostr.str(); 232 233 fclose(local); libssh2_session_free(session); closesocket(sock); 234 return -5; 235 } 236 237 //获取主机指纹 238 std::ostringstream ostr; 239 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 240 ostr << "Fingerprint: "; 241 for (int i = 0; i < 20; i++) { 242 unsigned char c = fingerprint[i]; 243 int nT = c;//这样转是为了防止符号位扩展 244 ostr << std::hex << std::setw(2) << std::setfill('0') << nT; 245 } 246 strLastError = ostr.str(); 247 248 //通过密码验证 249 if (libssh2_userauth_password(session, username.c_str(), password.c_str())) { 250 std::ostringstream ostr; 251 ostr << "[" << __FILE__ << "][" << __LINE__ << "] Authentication by password failed [" 252 << username << "][" << password << "]" << rc << std::endl; 253 strLastError = ostr.str(); 254 255 goto shutdown; 256 } 257 258 sftp_session = libssh2_sftp_init(session); 259 260 if (!sftp_session) { 261 std::ostringstream ostr; 262 ostr << "[" << __FILE__ << "][" << __LINE__ << "] Unable to init SFTP session" << std::endl; 263 strLastError = ostr.str(); 264 265 goto shutdown; 266 } 267 268 //向SFTP服务器发出新建文件请求 269 sftp_handle = 270 libssh2_sftp_open(sftp_session, remotepath.c_str(), 271 LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, 272 LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | 273 LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH); 274 275 if (!sftp_handle) { 276 std::ostringstream ostr; 277 ostr << "[" << __FILE__ << "][" << __LINE__ << "] Unable to open file with SFTP. ip=" 278 << ip <<"] remotepath=[" << remotepath << "]" << std::endl; 279 strLastError = ostr.str(); 280 281 nR = -1; 282 283 goto shutdown; 284 } 285 286 287 char mem[1024 * 16]; 288 size_t nread; 289 char *ptr; 290 size_t count = 0; 291 292 do { 293 nread = fread(mem, 1, sizeof(mem), local); 294 if (nread <= 0) { 295 //到达文件尾部 296 break; 297 } 298 ptr = mem; 299 do { 300 // 向服务器写数据,直到数据写完毕 301 rc = libssh2_sftp_write(sftp_handle, ptr, nread); 302 if (rc < 0) 303 break; 304 ptr += rc; count += nread; 305 nread -= rc; 306 307 //如果设置了回调,进行回调 308 if (m_bkCall) 309 { 310 float p = count / (float)filesize; 311 m_bkCall->OnProgress(p); 312 } 313 //callback.end 314 } while (nread); 315 316 if ( m_isBreak.load() == true ) 317 { 318 std::ostringstream ostr; 319 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 上传文件任务被用户break!" << std::endl; 320 strLastError = ostr.str(); 321 322 nR = -6; 323 break; 324 } 325 } while (rc > 0); 326 327 libssh2_sftp_close(sftp_handle); 328 libssh2_sftp_shutdown(sftp_session); 329 330 shutdown: 331 libssh2_session_disconnect(session, 332 "Normal Shutdown, Thank you for playing"); 333 libssh2_session_free(session); 334 335 closesocket(sock); 336 337 fclose(local); 338 339 return nR;//返回“0”表示成功 340 } 341 342 /* 343 源码参考地址 344 http://www.oschina.net/code/snippet_12_10717 345 */ 346 int SFTP_Libssh2::download(std::string ip, unsigned short port, std::string username, std::string password, 347 std::string sftppath, std::string localpath) 348 { 349 unsigned long hostaddr; 350 int sock, i, auth_pw = 0; 351 struct sockaddr_in sin; 352 const char *fingerprint; 353 char *userauthlist; 354 LIBSSH2_SESSION *session; 355 int rc; 356 LIBSSH2_SFTP *sftp_session; 357 LIBSSH2_SFTP_HANDLE *sftp_handle; 358 359 hostaddr = inet_addr(ip.c_str()); //hostaddr = htonl(0x7F000001); 360 361 /* 362 * The application code is responsible for creating the socket 363 * and establishing the connection 364 */ 365 sock = socket(AF_INET, SOCK_STREAM, 0); 366 367 sin.sin_family = AF_INET; 368 sin.sin_port = htons(port); 369 sin.sin_addr.s_addr = hostaddr; 370 if (connect(sock, (struct sockaddr*)(&sin), 371 sizeof(struct sockaddr_in)) != 0) { 372 std::ostringstream ostr; 373 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 连接失败!" << std::endl; 374 strLastError = ostr.str(); 375 return -1; 376 } 377 378 /* Create a session instance 379 */ 380 session = libssh2_session_init(); 381 382 if (!session) 383 return -1; 384 385 /* Since we have set non-blocking, tell libssh2 we are blocking */ 386 libssh2_session_set_blocking(session, 1); 387 388 389 /* ... start it up. This will trade welcome banners, exchange keys, 390 * and setup crypto, compression, and MAC layers 391 */ 392 rc = libssh2_session_handshake(session, sock); 393 394 if (rc) { 395 std::ostringstream ostr; 396 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 建立SSH会话失败" << rc << std::endl; 397 strLastError = ostr.str(); 398 399 return -1; 400 } 401 402 /* At this point we havn't yet authenticated. The first thing to do 403 * is check the hostkey's fingerprint against our known hosts Your app 404 * may have it hard coded, may go to a file, may present it to the 405 * user, that's your call 406 */ 407 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 408 409 std::ostringstream ostr; 410 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 411 ostr << "Fingerprint: "; 412 for (int i = 0; i < 20; i++) { 413 unsigned char c = fingerprint[i]; 414 int nT = c; 415 ostr << std::hex << std::setw(2) << std::setfill('0') << nT; 416 } 417 strLastError = ostr.str(); 418 419 /* check what authentication methods are available */ 420 userauthlist = libssh2_userauth_list(session, username.c_str(), username.length()); 421 if (strstr(userauthlist, "password") == NULL) 422 { 423 std::ostringstream ostr; 424 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 服务器不支持输入password方式验证!" << std::endl; 425 strLastError = ostr.str(); 426 goto shutdown; 427 } 428 429 /* We could authenticate via password */ 430 if (libssh2_userauth_password(session, username.c_str(), password.c_str())) { 431 432 std::ostringstream ostr; 433 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 密码错误!" << std::endl; 434 strLastError = ostr.str(); 435 goto shutdown; 436 } 437 438 sftp_session = libssh2_sftp_init(session); 439 if (!sftp_session) { 440 std::ostringstream ostr; 441 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 初始化FTL对话失败!" << std::endl; 442 strLastError = ostr.str(); 443 goto shutdown; 444 } 445 446 /* Request a file via SFTP */ 447 sftp_handle = 448 libssh2_sftp_open(sftp_session, sftppath.c_str(), LIBSSH2_FXF_READ, 0); 449 450 451 if (!sftp_handle) { 452 std::ostringstream ostr; 453 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 打开文件失败! " << libssh2_sftp_last_error(sftp_session) << std::endl; 454 strLastError = ostr.str(); 455 456 goto shutdown; 457 } 458 459 FILE *stream; 460 if (fopen_s(&stream, localpath.c_str(), "wb") == 0) 461 { 462 do { 463 char mem[1024]; 464 465 /* loop until we fail */ 466 rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem)); 467 468 if (rc > 0) { 469 //从内存到磁盘 470 fwrite(mem, 1, rc, stream); 471 } 472 else { 473 break; 474 } 475 } while (1); 476 477 fclose(stream); 478 } 479 else { 480 std::ostringstream ostr; 481 ostr << "[" << __FILE__ << "][" << __LINE__ << "] 新建本地文件失败 " << localpath << std::endl; 482 strLastError = ostr.str(); 483 } 484 485 libssh2_sftp_close(sftp_handle); 486 487 libssh2_sftp_shutdown(sftp_session); 488 489 shutdown: 490 491 libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing"); 492 libssh2_session_free(session); 493 494 closesocket(sock);//INVALID_SOCKET 495 496 return 0; 497 } 498 } 499 }
根据上述环境编译成功的libssh2.lib,csdn下载地址:https://download.csdn.net/download/zkfopen/10606098