參考鏈接: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