linux C++使用libssh2实现SFTP下载和FTP下载

最近写了个sftp和ftp下载的接口, 这里做个简单记录,FTP下载参考了一下网上的例子,SFTP则是用了libssh2的接口,涉及到openssl和libssh2的编译我再另一个笔记中有记录,这里贴一下链接:https://www.cnblogs.com/TssiNG-Z/p/15839297.html

关于FTP下载的参考, 链接: https://blog.csdn.net/swartz_lubel/article/details/57115820

关于FTP有一点要提及, FTP下载分为主动模式(服务器主动连接至客户端进行数据传输), 被动模式(服务器开放一个端口, 客户端新增一个连接至该端口进行数据传输), 这里我选择的下载模式是被动模式, 被动模式服务器开放的端口的计算方式为 port = port_param1 * 256 + port_param2

下面直接贴代码

  1 /******************Download.h*************************
  2 *
  3 * brief   : ftp/sftp 下载
  4 *
  5 * author  : tsing
  6 *
  7 * date    : 20220125
  8 *
  9 * version : 1.0
 10 *
 11 ********************************************/
 12 
 13 #ifndef __DOWNLOAD_
 14 #define __DOWNLOAD_
 15 
 16 #include <stdio.h>
 17 #include <string.h>
 18 #include <string>
 19 #include <fstream>
 20 
 21 using namespace std;
 22 
 23 #include <unistd.h>
 24 #include <stdlib.h>
 25 #include <fcntl.h>
 26 #include <sys/types.h>
 27 #include <sys/time.h>
 28 #include <sys/stat.h>
 29 #include <sys/socket.h>
 30 #include <sys/ioctl.h>
 31 #include <sys/select.h>
 32 #include <netinet/in.h>
 33 #include <arpa/inet.h>
 34 
 35 #include "libssh2.h"
 36 #include "libssh2_publickey.h"
 37 #include "libssh2_sftp.h"
 38 
 39 #define FTPBUF 4096
 40 #define FTP_CONNECT_FAIL   -1
 41 #define FTP_USERNAME_FAIL  -2
 42 #define FTP_PASSWORD_FAIL  -3
 43 #define FTP_PASSIVE_FAIL   -4
 44 #define FTP_DATALINK_FAIL  -5
 45 #define FTP_REQUIRE_FAIL   -6
 46 #define FTP_LOCALFILE_FAIL -7
 47 
 48 #define SFTP_LIBSSH2_FAIL     -1
 49 #define SFTP_SESSION_FAIL     -2
 50 #define SFTP_HANDSHAKE_FAIL   -3
 51 #define SFTP_LOGINMETHOD_FAIL -4
 52 #define SFTP_LOGIN_FAIL       -5
 53 #define SFTP_INIT_FAIL        -6
 54 #define SFTP_GETREMOTE_FAIL   -7
 55 #define SFTP_OPENFILE_FAIL    -8
 56 
 57 class CTcpClient
 58 {
 59 public:
 60     CTcpClient();
 61     virtual ~CTcpClient();
 62 
 63     /*
 64     * @prief 建立连接
 65     * @param [in] s_ip : ip地址
 66     * @param [in] i_port  : 端口号
 67     * @return [-1 : 失败]  [0 : 成功]
 68     */
 69     int to_connect(const string &s_ip, const int &i_port);
 70 
 71     /*
 72     * @prief 断开连接
 73     * @return [-1 : 失败]  [0 : 成功]
 74     */
 75     int to_disconnect();
 76 
 77     /*
 78     * @prief 接收数据
 79     * @param [in] szbuf : 接收缓冲区
 80     * @param [in] ilen  : 缓冲区长度
 81     * @return [-1 : 失败]  [>= 0 : 接收到的长度]
 82     */
 83     int to_recv(char *szbuf, const int &ilen);
 84 
 85     /*
 86     * @prief 发送数据
 87     * @param [in] szsend : 待发送的内容
 88     * @param [in] ilen  : 发送内容的长度
 89     * @return [-1 : 失败]  [0 : 成功]
 90     */
 91     int to_send(const char *szsend, const int &ilen);
 92 
 93     /*
 94     * @prief 交换数据
 95     * @param [in] szsend : 待发送的内容
 96     * @param [in] sendlen  : 发送内容的长度
 97     * @param [in] szbuf : 接收的内容
 98     * @param [in] recvlen  : 接收内容的长度
 99     * @return [-1 : 失败]  [0 : 成功]
100     */
101     int to_exchange(const char *szsend, const int &sendlen, char *szbuf, const int &recvlen);
102 
103     /*
104     * @prief 获取套接字描述符
105     * @return [int : 套接字] [-1 : 未连接]
106     */
107     int get_sockfd();
108 
109 private:
110     int  _m_sockfd;
111     bool _m_isconnect;
112 };
113 
114 int ftp_getfile(const string &ip, const int &port, const string &username, const string &pwd, const string &remote_file, const string &local_file, char *szMsg);
115 
116 int sftp_getfile(const string &ip, const int &port, const string &username, const string &pwd, const string &remote_file, const string &local_file, char *szMsg);
117 
118 #endif
119 
120 /*****************************************Download.cpp**************************************************/
121 
122 #include "Download.h"
123 bool str_replace(string &s, const string &symbol, const string &replace)
124 {
125     if (s.empty() || symbol.empty()) return false;
126 
127     for (size_t pos = s.find(symbol); pos != string::npos; pos = s.find(symbol, pos))
128     {
129         if (replace.empty())
130         {
131             s.erase(pos, symbol.length());
132         }
133         else
134         {
135             s.replace(pos, symbol.length(), replace);
136         }
137     }
138     return true;
139 }
140 
141 bool ftp2link(const string &ftp_msg, string &ip, int &port)
142 {
143     if (ftp_msg.empty()) return false;
144 
145     size_t start_pos = ftp_msg.find("(");
146     size_t end_pos   = ftp_msg.find(")");
147     string link_info  = ftp_msg.substr((start_pos + 1), (end_pos - start_pos - 1));
148 
149     str_replace(link_info, ",", ".");
150     start_pos = -1;
151     for (int i = 0; i < 4; i++)
152     {
153         start_pos = link_info.find(".", start_pos + 1);
154     }
155 
156     ip = link_info.substr(0, start_pos);
157     string port0 = link_info.substr(start_pos + 1, link_info.rfind(".") - start_pos - 1);
158     string port1 = link_info.substr(link_info.rfind(".") + 1,  string::npos);
159     port = (atoi(port0.c_str()) * 256 + atoi(port1.c_str()));
160 
161     printf("ip[%s] port[%d] port0[%s] port1[%s]\n", ip.c_str(), port, port0.c_str(), port1.c_str());
162     return true;
163 }
164 
165 CTcpClient::CTcpClient()
166 {
167     _m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
168 }
169 
170 CTcpClient::~CTcpClient()
171 {
172     if (_m_isconnect)
173     {
174         close(_m_sockfd);
175     }
176 }
177 
178 int CTcpClient::to_connect(const string &s_ip, const int &i_port)
179 {
180     if (_m_sockfd == -1)
181     {
182         _m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
183         if (_m_sockfd == -1) return -1;
184     }
185 
186     ioctl(_m_sockfd, FIONBIO, 1);
187 
188     sockaddr_in addr_info;
189     memset(&addr_info, 0, sizeof(sockaddr_in));
190 
191     addr_info.sin_family = AF_INET;
192     addr_info.sin_addr.s_addr = inet_addr(s_ip.c_str());
193     addr_info.sin_port = htons(i_port);
194 
195     int ret_val = connect(_m_sockfd, (sockaddr*)(&addr_info), sizeof(sockaddr));
196     if (ret_val == -1)
197     {
198         timeval timeout;
199         timeout.tv_sec = 10;
200         timeout.tv_usec = 0;
201         fd_set readable_set;
202         FD_ZERO(&readable_set);
203         FD_SET(_m_sockfd, &readable_set);
204 
205         int errno = 0;
206         int len   = sizeof(int);
207         if (0 < select(_m_sockfd + 1, 0, &readable_set, 0, &timeout))
208         {
209             getsockopt(_m_sockfd, SOL_SOCKET, SO_ERROR, &errno, (socklen_t*)(&len));
210             if (errno == 0)
211             {
212                 ioctl(_m_sockfd, FIONBIO, 0);
213                 _m_isconnect = true;
214                 return 0;
215             }
216         }
217 
218         _m_isconnect = false;
219         return -1;
220     }
221 
222     ioctl(_m_sockfd, FIONBIO, 0);
223     _m_isconnect = true;
224     return 0;
225 }
226 
227 int CTcpClient::to_disconnect()
228 {
229     if (_m_isconnect)
230     {
231         close(_m_sockfd);
232         return 0;
233     }
234     return -1;
235 }
236 
237 int CTcpClient::to_recv(char *szbuf, const int &ilen)
238 {
239     if (_m_sockfd == -1) return -1;
240 
241     //select超时时间
242     timeval timeout;
243     timeout.tv_sec  = 1;
244     timeout.tv_usec = 0;
245 
246     //可读事件fd集合
247     fd_set readable_list;
248     FD_ZERO(&readable_list);
249     FD_SET(_m_sockfd, &readable_list);
250 
251     if (0 < select(_m_sockfd + 1, &readable_list, 0, 0, &timeout))
252     {
253         return (recv(_m_sockfd, szbuf, ilen, 0));
254     }
255 
256     return -1;
257 }
258 
259 int CTcpClient::to_send(const char *szsend, const int &ilen)
260 {
261     if (_m_sockfd == -1) return -1;
262 
263     timeval timeout;
264     timeout.tv_sec  = 1;
265     timeout.tv_usec = 0;
266 
267     fd_set writable_list;
268     FD_ZERO(&writable_list);
269     FD_SET(_m_sockfd, &writable_list);
270 
271     if (0 < select(_m_sockfd + 1, 0, &writable_list, 0, &timeout))
272     {
273         int i_already_send = 0;
274         int i_send_cnt     = 0;
275         while (i_already_send != ilen)
276         {
277             i_send_cnt = send(_m_sockfd, szsend, ilen, 0);
278             if (i_send_cnt == -1) return -1;
279 
280             i_already_send += i_send_cnt;
281         }
282 
283         return 0;
284     }
285 
286     return -1;
287 }
288 
289 int CTcpClient::to_exchange(const char *szsend, const int &sendlen, char *szbuf, const int &recvlen)
290 {
291     int ret = 0;
292 
293     ret = to_send(szsend, sendlen);
294     if (ret == -1)
295     {
296         printf("tcp exchange send failure\n");
297         return -1;
298     }
299 
300     ret = to_recv(szbuf, recvlen);
301     if (ret > 0)
302     {
303         printf("tcp exchange with response : %s\n", szbuf);
304     }
305     else
306     {
307         printf("tcp exchange with no response\n");
308         return -1;
309     }
310     return 0;
311 }
312 
313 int CTcpClient::get_sockfd()
314 {
315     if (_m_isconnect)
316     {
317         return _m_sockfd;
318     }
319 
320     return -1;
321 }
322 
323 int ftp_getfile(const string &ip, const int &port, const string &username, const string &pwd, const string &remote_file, const string &local_file, char *szMsg)
324 {
325     int        ftp_ret          =   0  ;  //接口返回值
326     char       ftp_recv[FTPBUF] = { 0 };  //FTP接收缓存
327     string     ftp_cmd          =  ""  ;  //FTP控制指令
328     CTcpClient cmd_link;                  //指令连接,用于传输控制消息
329     CTcpClient data_link;                 //数据连接,用于传输数据
330 
331     //打开tcp通道
332     ftp_ret = cmd_link.to_connect(ip, port);
333     if (ftp_ret == -1)
334     {
335         sprintf(szMsg, "ftp connect fail...");
336         return FTP_CONNECT_FAIL;
337     }
338 
339     //接收登录消息
340     ftp_ret = cmd_link.to_recv(ftp_recv, FTPBUF);
341     if (ftp_ret > 0)
342     {
343         printf("ftp sock open success with response : %s\n", ftp_recv);
344     }
345     else
346     {
347         printf("ftp sock open success with no response\n");
348     }
349     
350     //发送登录用户名
351     ftp_cmd = "USER ";
352     ftp_cmd += (username + "\n");
353     memset(ftp_recv, 0, FTPBUF);
354     if (0 != cmd_link.to_exchange(ftp_cmd.c_str(), ftp_cmd.length(), ftp_recv, FTPBUF))
355     {
356         sprintf(szMsg, "ftp send username [%s] fail [%s]", username.c_str(), ftp_recv);
357         return FTP_USERNAME_FAIL;
358     }
359 
360     //发送登录密码
361     ftp_cmd = "PASS ";
362     ftp_cmd += (pwd + "\n");
363     memset(ftp_recv, 0, FTPBUF);
364     if (0 != cmd_link.to_exchange(ftp_cmd.c_str(), ftp_cmd.length(), ftp_recv, FTPBUF))
365     {
366         sprintf(szMsg, "ftp send password [%s/%s] fail [%s]", username.c_str(), pwd.c_str(), ftp_recv);
367         return FTP_PASSWORD_FAIL;
368     }
369 
370     //设置为被动模式
371     ftp_cmd = "PASV\n";
372     memset(ftp_recv, 0, FTPBUF);
373     if (0 != cmd_link.to_exchange(ftp_cmd.c_str(), ftp_cmd.length(), ftp_recv, FTPBUF))
374     {
375         sprintf(szMsg, "ftp set fassive fail [%s]", ftp_recv);
376         return FTP_PASSIVE_FAIL;
377     }
378 
379     string datalink_ip   = "";
380     int    datalink_port = 0 ;
381 
382     //解析数据连接基本信息
383     ftp2link(ftp_recv, datalink_ip, datalink_port);
384 
385     //打开数据连接
386     ftp_ret = data_link.to_connect(datalink_ip, datalink_port);
387     if (ftp_ret == -1)
388     {
389         sprintf(szMsg, "ftp data link open fail");
390         return FTP_DATALINK_FAIL;
391     }
392 
393     //发送下载请求
394     ftp_cmd = "RETR ";
395     ftp_cmd += (remote_file + "\n");
396     memset(ftp_recv, 0, FTPBUF);
397     if (0 != cmd_link.to_exchange(ftp_cmd.c_str(), ftp_cmd.length(), ftp_recv, FTPBUF))
398     {
399         sprintf(szMsg, "ftp require file [%s] fail [%s]", remote_file.c_str(), ftp_recv);
400         return FTP_REQUIRE_FAIL;
401     }
402 
403     //打开本地文件
404     FILE *fp = fopen(local_file.c_str(), "wb+");
405     if (fp != NULL)
406     {
407         int recv_len = 0;
408         char mem[40960] = { 0 };
409         
410         //将数据连接中的数据写入文件中
411         while (0 < (recv_len = data_link.to_recv(mem, sizeof(mem))))
412         {
413             fwrite(mem, 1, recv_len, fp);
414             fflush(fp);
415             memset(mem, 0, sizeof(mem));
416         }
417 
418         fclose(fp);
419     }
420     else
421     {
422         sprintf(szMsg, "ftp create local file [%s] fail", local_file.c_str());
423         return FTP_LOCALFILE_FAIL;
424     }
425 
426     ftp_cmd = "QUIT\n";
427     memset(ftp_recv, 0, FTPBUF);
428 
429     cmd_link.to_exchange(ftp_cmd.c_str(), ftp_cmd.length(), ftp_recv, FTPBUF);
430     sprintf(szMsg, "ftp download success [%s]", ftp_recv);
431     return 0;
432 }
433 
434 
435 int sftp_getfile(const string &ip, const int &port, const string &username, const string &pwd, const string &remote_file, const string &local_file, char *szMsg)
436 {
437     int i = 0, auth_pw = 0;
438     const char *fingerprint;
439     char *userauthlist;
440     LIBSSH2_SESSION *session = NULL;
441     LIBSSH2_SFTP *sftp_session = NULL;
442     LIBSSH2_SFTP_HANDLE *sftp_handle = NULL;
443     FILE *fp = NULL;
444     CTcpClient tcplink;
445 
446     //libssh2初始化
447     int rc = libssh2_init (0); 
448     if (rc != 0) {        
449         sprintf(szMsg, "libssh2 init fail");
450         return SFTP_LIBSSH2_FAIL;
451     }  
452     
453     //建立tcp连接
454     tcplink.to_connect(ip, port);
455     
456     //建立ssh通讯session
457     session = libssh2_session_init();
458     if(!session)
459     {
460         sprintf(szMsg, "libssh2 start session fail");
461         return SFTP_SESSION_FAIL;
462     }
463     
464     //阻塞通讯模式
465     libssh2_session_set_blocking(session, 1);
466     
467     //ssh交换密钥等操作
468     rc = libssh2_session_handshake(session, tcplink.get_sockfd());
469     if(rc)
470     {        
471         sprintf(szMsg, "libssh2 session handshake fail");
472         return SFTP_HANDSHAKE_FAIL;
473     }
474   
475     //密钥认证
476     fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
477     
478     //检查服务器可用的登录方式
479     userauthlist = libssh2_userauth_list(session, username.c_str(), username.length());
480     if (strstr(userauthlist, "password") != NULL)
481     {
482         printf("libssh2 server with support login method [%s]\n", userauthlist);
483     }
484     else
485     {
486         sprintf(szMsg, "libssh2 server not support username/password login");
487         return SFTP_LOGINMETHOD_FAIL;
488     }
489 
490 
491     //密码登录
492     if (libssh2_userauth_password(session, username.c_str(), pwd.c_str()))
493     {
494         sprintf(szMsg, "libssh2 login fail with username[%s] password[%s]", username.c_str(), pwd.c_str());
495         return SFTP_LOGIN_FAIL;
496     } 
497  
498     sftp_session = libssh2_sftp_init(session);  
499     if (!sftp_session)
500     {
501         sprintf(szMsg, "libssh2 sftp init fail");
502         return SFTP_INIT_FAIL;
503     }
504 
505     sftp_handle = libssh2_sftp_open(sftp_session, remote_file.c_str(), LIBSSH2_FXF_READ, 0);
506     if (!sftp_handle)
507     {
508         sprintf(szMsg, "libssh2 remote file open fail [%s]", remote_file.c_str());
509         return SFTP_GETREMOTE_FAIL;
510     }    
511 
512     fp = fopen(local_file.c_str(),"wb+");
513     if(fp == NULL)
514     {
515         sprintf(szMsg, "local file open fail [%s]", local_file.c_str());
516         return SFTP_OPENFILE_FAIL;
517     }
518 
519     char mem[40960] = { 0 };
520     while (0 < (rc = libssh2_sftp_read(sftp_handle, mem, sizeof(mem))))
521     {
522         fwrite(mem, rc, 1, fp);
523         memset(mem, 0, sizeof(mem));
524     }
525     fclose(fp);
526 
527     if (sftp_handle)
528         libssh2_sftp_close(sftp_handle);
529     if (sftp_session)
530         libssh2_sftp_shutdown(sftp_session);
531    
532     if (session)
533     {
534         libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
535         libssh2_session_free(session);
536     }
537      
538     libssh2_exit();    
539     tcplink.to_disconnect();
540     return 0;
541 }

以上, 如有错误疏漏等, 欢迎讨论指点, 感谢.

posted @ 2022-02-08 14:51  public_tsing  阅读(3508)  评论(0编辑  收藏  举报