最近写了个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 }
以上, 如有错误疏漏等, 欢迎讨论指点, 感谢.
原文链接: https://www.cnblogs.com/TssiNG-Z/p/15871159.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/187779
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!