SFTP用戶端程式碼範例,sftp用戶端樣本
SFTP用戶端程式碼範例
環境:libssh2 1.4.3、zlib-1.2.8、openssl-1.0.1g
Author: Kagula
最後更新日期:2014-5-18
從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路徑下
下面是SFTP用戶端範例程式碼
#include "SFTP_Libssh2.h"#include <iostream>int main(int argc, char* argv[]){//下面的代碼只要在進程初始化的時候執行kagula::network::SFTP_Init();//測試SFTP連結kagula::network::SFTP_Libssh2* client = kagula::network::SFTP_Libssh2::Inst();std::string ip = "192.168.19.130";uint16_t port = 22;std::string usr = "kagula";std::string pwd = "123456";if (false == client->IsAbilityConn(ip, port, usr, pwd)){std::cout << client->strLastError << std::endl;return -1;}//測試檔案上傳,d:\\temp\\a.htmlif (0 != client->upload(ip, 22, usr, pwd, "d:\\temp\\a.html", "/home/kagula/a.html")){std::cout << "Error:" << client->strLastError << std::endl;} else{std::cout << client->strLastError << std::endl;}//測試檔案下載if (0 != client->download(ip, 22, usr, pwd, "/home/kagula/a.html","d:\\temp\\b.html" )){std::cout << "Error:" << client->strLastError << std::endl;}else{std::cout << client->strLastError << std::endl;}//進程準備結束,釋放資源的時候,運行下面的代碼kagula::network::SFTP_Exit();return 0;}
SFTP_Libssh2.h
#pragma once#include <string>#include <atomic>/*功能:SFTP協議的檔案傳輸功能最後更新日期:2014-5-17簡介:藉助Libssh2庫很容易實現sftp,ssh2用戶端,這裡給出 如何?Sftp用戶端的代碼測試環境:Windows 8.1 64bit、Visual Studio 2013 Professional SP1 OpenSSL 1.0.1g、zlib-1.2.8、libssh2 1.4.3 Win32控制台項目注意:動態連結需要把“libssh2.dll”檔案複製到當前項目路徑下說明:原來的代碼支援多線程,從應用程式抽出來的時候簡化了, 你可以修改代碼使它同時支援上傳或下載多個檔案。建議:[1]第三方庫直接下載原始碼自己編譯免得庫由於編譯器版本的 不同或設定的不同連結的時候一大堆麻煩。 [2]讀懂代碼根據項目需求作相應修改補充閱讀資料:《使用libssh2庫實現支援密碼參數的ssh2用戶端》http://blog.chinaunix.net/uid-24382173-id-229823.html*/namespace kagula {namespace network {int SFTP_Init();void SFTP_Exit();class SFTP_BKCall{public:/* progress傳回值範圍[0.0,1.0] */virtual void OnProgress(float progress) = 0;};class SFTP_Libssh2{public:static SFTP_Libssh2* Inst(){static SFTP_Libssh2 inst;return &inst;}/*入口參數使用說明ip: 就填一個IP地址就好了,例如“127.0.0.1”。port: 連接埠,SFTP伺服器預設連接埠為22。username:password:sftppath: 遠程路徑“/”開頭 ,例如“/a.jpg”localpath: 本地路徑,例如“d:\\temp\\test.jpg”strLastError: 錯誤資訊出口參數返回不等於零,代表失敗!*/int upload(std::string ip, unsigned short port, std::string username,std::string password, std::string localpath, std::string remotepath);int download(std::string ip, unsigned short port, std::string username,std::string password, std::string sftppath, std::string localpath);//測試SFTP伺服器是否可以連結bool IsAbilityConn(std::string ip, unsigned short port, std::string username,std::string password);//設定回掉函數void SetBKCall(SFTP_BKCall *bkCall){ m_bkCall = bkCall; }//存放最近的錯誤資訊std::string strLastError;//用於停止當前上傳或下載線程void stop() { m_isBreak.store(true); }private:SFTP_Libssh2() :m_bkCall(NULL) { m_isBreak.store(false); };//防止直接初始化SFTP_Libssh2(const SFTP_Libssh2&); //防止拷貝複製SFTP_Libssh2& operator=(const SFTP_Libssh2&); //防止分配(運算子函數的調用)SFTP_BKCall *m_bkCall;std::atomic_bool m_isBreak; //帶讀防寫保護的bool值};}}
SFTP_Libssh2.cpp
//SFTP_Libssh2.cpp檔案清單#include "SFTP_Libssh2.h"#include <libssh2.h>#include <libssh2_sftp.h>#ifdef HAVE_WINSOCK2_H#include <winsock2.h>#endif#ifdef HAVE_SYS_SOCKET_H#include <sys/socket.h>#endif#ifdef HAVE_NETINET_IN_H#include <netinet/in.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#ifdef HAVE_ARPA_INET_H#include <arpa/inet.h>#endif#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#include <sys/types.h>#include <fcntl.h>#include <errno.h>#include <stdio.h>#include <ctype.h>#include <sstream>#include <iomanip>#pragma comment(lib, "ws2_32.lib")#pragma comment(lib, "libeay32.lib") #pragma comment(lib, "libssh2.lib") namespace kagula {namespace network{//初始化進程的時候調用//如果非0表示初始化失敗!int SFTP_Init(){WSADATA wsadata;int rc = WSAStartup(MAKEWORD(2, 0), &wsadata);if (rc != 0) {return rc;}rc = libssh2_init(0);return rc;}//進程結束的時候調用void SFTP_Exit(){libssh2_exit();WSACleanup();}bool SFTP_Libssh2::IsAbilityConn(std::string ip, unsigned short port, std::string username,std::string password){unsigned long hostaddr;struct sockaddr_in sin;const char *fingerprint;LIBSSH2_SESSION *session;int rc;bool bR = false;FILE *local;LIBSSH2_SFTP *sftp_session;LIBSSH2_SFTP_HANDLE *sftp_handle;hostaddr = inet_addr(ip.c_str());//hostaddr = htonl(0x7F000001);//建立串連int sock = socket(AF_INET, SOCK_STREAM, 0);sin.sin_family = AF_INET;sin.sin_port = htons(port);sin.sin_addr.s_addr = hostaddr;if (connect(sock, (struct sockaddr*)(&sin),sizeof(struct sockaddr_in)) != 0) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "]failed to connect" << ip << "!" << std::endl;strLastError = ostr.str();return bR;}//建立對話執行個體session = libssh2_session_init();if (!session){closesocket(sock);return bR;}//設定調用阻塞libssh2_session_set_blocking(session, 1);//進行握手rc = libssh2_session_handshake(session, sock);if (rc) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "]Failure establishing SSH session: " << rc << std::endl;strLastError = ostr.str();libssh2_session_free(session); closesocket(sock);return bR;}//檢查主機指紋std::ostringstream ostr;fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);ostr << "Fingerprint: ";for (int i = 0; i < 20; i++) {unsigned char c = fingerprint[i];int nT = c;ostr << std::hex << std::setw(2) << std::setfill('0') << nT;}strLastError = ostr.str();//通過密碼驗證登陸使用者身份if (libssh2_userauth_password(session, username.c_str(), password.c_str())) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "]Authentication by password failed." << std::endl;strLastError = ostr.str();goto shutdown;}sftp_session = libssh2_sftp_init(session);if (!sftp_session) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "]Unable to init SFTP session " << std::endl;strLastError = ostr.str();goto shutdown;}bR = true;libssh2_sftp_shutdown(sftp_session);shutdown:libssh2_session_disconnect(session,"Normal Shutdown, Thank you for playing");libssh2_session_free(session);closesocket(sock);return bR;}/*源碼參考地址http://www.libssh2.org/examples/sftp_write.html*/int SFTP_Libssh2::upload(std::string ip, unsigned short port, std::string username, std::string password,std::string localpath, std::string remotepath){if (ip.length()<1 || username.length()<1 || password.length()<1 || localpath.length()<1 || remotepath.length()<1){return -1;}int nR = 0;unsigned long hostaddr;struct sockaddr_in sin;const char *fingerprint;LIBSSH2_SESSION *session;int rc = -1;FILE *local = NULL;LIBSSH2_SFTP *sftp_session;LIBSSH2_SFTP_HANDLE *sftp_handle;hostaddr = inet_addr(ip.c_str());//hostaddr = htonl(0x7F000001);if (fopen_s(&local, localpath.c_str(), "rb") != 0) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "]Can't open local file " << localpath << std::endl;strLastError = ostr.str();return -2;}//取待上傳檔案整個size.fseek(local, 0, SEEK_END);size_t filesize = ftell(local);//local file大小,在readFromDisk中被引用fseek(local, 0, SEEK_SET);//檔案指標重設到檔案頭//建立串連int sock = socket(AF_INET, SOCK_STREAM, 0);sin.sin_family = AF_INET;sin.sin_port = htons(port);sin.sin_addr.s_addr = hostaddr;if (connect(sock, (struct sockaddr*)(&sin),sizeof(struct sockaddr_in)) != 0) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] failed to connect " << ip << std::endl;strLastError = ostr.str();fclose(local);return -3;}//建立會話執行個體session = libssh2_session_init();if (!session){fclose(local); closesocket(sock);return -4;}//阻塞方式調用libssh2libssh2_session_set_blocking(session, 1);//進行握手rc = libssh2_session_handshake(session, sock);if (rc) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] Failure establishing SSH session:" << rc << std::endl;strLastError = ostr.str();fclose(local); libssh2_session_free(session); closesocket(sock);return -5;}//擷取主機指紋std::ostringstream ostr;fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);ostr << "Fingerprint: ";for (int i = 0; i < 20; i++) {unsigned char c = fingerprint[i];int nT = c;//這樣轉是為了防止符號位擴充ostr << std::hex << std::setw(2) << std::setfill('0') << nT;}strLastError = ostr.str();//通過密碼驗證if (libssh2_userauth_password(session, username.c_str(), password.c_str())) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] Authentication by password failed ["<< username << "][" << password << "]" << rc << std::endl;strLastError = ostr.str();goto shutdown;}sftp_session = libssh2_sftp_init(session);if (!sftp_session) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] Unable to init SFTP session" << std::endl;strLastError = ostr.str();goto shutdown;}//向SFTP伺服器發出建立檔案請求sftp_handle =libssh2_sftp_open(sftp_session, remotepath.c_str(),LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC,LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR |LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);if (!sftp_handle) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] Unable to open file with SFTP. ip=" << ip <<"] remotepath=[" << remotepath << "]" << std::endl;strLastError = ostr.str();nR = -1;goto shutdown;}char mem[1024 * 16];size_t nread;char *ptr;size_t count = 0;do {nread = fread(mem, 1, sizeof(mem), local);if (nread <= 0) {//到達檔案尾部break;}ptr = mem;do {// 向伺服器寫資料,直到資料寫完畢rc = libssh2_sftp_write(sftp_handle, ptr, nread);if (rc < 0)break;ptr += rc; count += nread;nread -= rc;//如果設定了回調,進行回調if (m_bkCall){float p = count / (float)filesize;m_bkCall->OnProgress(p);}//callback.end} while (nread);if ( m_isBreak.load() == true ){std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 上傳檔案任務被使用者break!" << std::endl;strLastError = ostr.str();nR = -6;break;}} while (rc > 0);libssh2_sftp_close(sftp_handle);libssh2_sftp_shutdown(sftp_session);shutdown:libssh2_session_disconnect(session,"Normal Shutdown, Thank you for playing");libssh2_session_free(session);closesocket(sock);fclose(local);return nR;//返回“0”表示成功}/*源碼參考地址http://www.oschina.net/code/snippet_12_10717*/int SFTP_Libssh2::download(std::string ip, unsigned short port, std::string username, std::string password,std::string sftppath, std::string localpath){unsigned long hostaddr;int sock, i, auth_pw = 0;struct sockaddr_in sin;const char *fingerprint;char *userauthlist;LIBSSH2_SESSION *session;int rc;LIBSSH2_SFTP *sftp_session;LIBSSH2_SFTP_HANDLE *sftp_handle;hostaddr = inet_addr(ip.c_str()); //hostaddr = htonl(0x7F000001);/** The application code is responsible for creating the socket* and establishing the connection*/sock = socket(AF_INET, SOCK_STREAM, 0);sin.sin_family = AF_INET;sin.sin_port = htons(port);sin.sin_addr.s_addr = hostaddr;if (connect(sock, (struct sockaddr*)(&sin),sizeof(struct sockaddr_in)) != 0) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 串連失敗!" << std::endl;strLastError = ostr.str();return -1;}/* Create a session instance*/session = libssh2_session_init();if (!session)return -1;/* Since we have set non-blocking, tell libssh2 we are blocking */libssh2_session_set_blocking(session, 1);/* ... start it up. This will trade welcome banners, exchange keys,* and setup crypto, compression, and MAC layers*/rc = libssh2_session_handshake(session, sock);if (rc) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 建立SSH會話失敗" << rc << std::endl;strLastError = ostr.str();return -1;}/* At this point we havn't yet authenticated. The first thing to do* is check the hostkey's fingerprint against our known hosts Your app* may have it hard coded, may go to a file, may present it to the* user, that's your call*/fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);std::ostringstream ostr;fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);ostr << "Fingerprint: ";for (int i = 0; i < 20; i++) {unsigned char c = fingerprint[i];int nT = c;ostr << std::hex << std::setw(2) << std::setfill('0') << nT;}strLastError = ostr.str();/* check what authentication methods are available */userauthlist = libssh2_userauth_list(session, username.c_str(), username.length());if (strstr(userauthlist, "password") == NULL){std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 伺服器不支援輸入password方式驗證!" << std::endl;strLastError = ostr.str();goto shutdown;}/* We could authenticate via password */if (libssh2_userauth_password(session, username.c_str(), password.c_str())) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 密碼錯誤!" << std::endl;strLastError = ostr.str();goto shutdown;}sftp_session = libssh2_sftp_init(session);if (!sftp_session) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 初始化FTL對話失敗!" << std::endl;strLastError = ostr.str();goto shutdown;}/* Request a file via SFTP */sftp_handle =libssh2_sftp_open(sftp_session, sftppath.c_str(), LIBSSH2_FXF_READ, 0);if (!sftp_handle) {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 開啟檔案失敗! " << libssh2_sftp_last_error(sftp_session) << std::endl;strLastError = ostr.str();goto shutdown;}FILE *stream;if (fopen_s(&stream, localpath.c_str(), "wb") == 0){do {char mem[1024];/* loop until we fail */rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem));if (rc > 0) {//從記憶體到磁碟fwrite(mem, 1, rc, stream);}else {break;}} while (1);fclose(stream);}else {std::ostringstream ostr;ostr << "[" << __FILE__ << "][" << __LINE__ << "] 建立本地檔案失敗 " << localpath << std::endl;strLastError = ostr.str();}libssh2_sftp_close(sftp_handle);libssh2_sftp_shutdown(sftp_session);shutdown:libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");libssh2_session_free(session);closesocket(sock);//INVALID_SOCKETreturn 0;}}}
我要上傳檔案到sftp上,c#代碼怎指定sftp的連接埠?
new Tamir.SharpSsh.java.String
用到這個 還要手動指定連接埠之類的屬性 我只瞭解 但是寫不出來。。
SFTP用什密碼編譯演算法
SFTP (SSH或Secure FTP) 是基於 SSH 來進行加密和驗證的. 而 SSH 是通過公開祕密金鑰加密演算法來驗證和加密的。具體的SSH標準有
* RFC 4250, The Secure Shell (SSH) Protocol Assigned Numbers
* RFC 4251, The Secure Shell (SSH) Protocol Architecture
* RFC 4252, The Secure Shell (SSH) Authentication Protocol
* RFC 4253, The Secure Shell (SSH) Transport Layer Protocol
* RFC 4254, The Secure Shell (SSH) Connection Protocol
* RFC 4255, Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
* RFC 4256, Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)
* RFC 4335, The Secure Shell (SSH) Session Channel Break Extension
* RFC 4344, The Secure Shell (SSH) Transport Layer Encryption Modes
* RFC 4345, Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
具體的SSH/SFTP代碼可以去研究開源的 openSSH 源碼
www.openssh.com/