上一篇FTP客户端讲到如果制作一个简单的FTP客户端,功能实现了,但是后面我们发现了问题,就是FTP是使用明文进行操作的。对于普通情况来说就无所谓了。但有时候要安全的一点的话,就应该使用FTP的安全版本。有SFTP和FTPs,两者都是FTP的安全版本,但是两者的实现原理差别还是很大的,具体自己搜索了解。
0.环境安装
环境使用我的这一篇文章安装好libssh2库。
http://www.cnblogs.com/wunaozai/p/4528394.html
使用一个带有SFTP功能的FTP服务器。注意有些FTP服务器是不带SFTP功能的。这里我使用这个FreeSSHd作为SFTP服务器。
http://www.freesshd.com/?ctt=download
关于freesshd配置说两句,Server status标签 点击确定SSH server is running。SSH标签,确定配置完成。Authentication标签下Password authentication为Allowed,Public key authentication为Disabled,这样做的原因是我接下来要做的程序只支持密码登录,减少不必要的干扰,如果有需要,可以自己设定。Tunneling标签,所有选项选中,如果没有选中,本地如果网络复杂的话,可能会有问题。SFTP标签,选择一个作为FTP的根目录。Users标签,增加一个用户。基本设置就这些了。
1.示例讲解
我们先从libssh2中的示例程序进行讲解,libssh2源代码中的example文件夹中有几个常见的示例程序,我们此次先讲解上传文件和下载文件这两个基础功能。
下面这个是sftp_write_nonblock.c的源代码,已被折叠。
1 /* 2 * Sample showing how to do SFTP non-blocking write transfers. 3 * 4 * The sample code has default values for host name, user name, password 5 * and path to copy, but you can specify them on the command line like: 6 * 7 * "sftp 192.168.0.1 user password sftp_write_nonblock.c /tmp/sftp_write_nonblock.c" 8 */ 9 10 #include "libssh2_config.h" 11 #include <libssh2.h> 12 #include <libssh2_sftp.h> 13 14 #ifdef HAVE_WINSOCK2_H 15 # include <winsock2.h> 16 #endif 17 #ifdef HAVE_SYS_SOCKET_H 18 # include <sys/socket.h> 19 #endif 20 #ifdef HAVE_NETINET_IN_H 21 # include <netinet/in.h> 22 #endif 23 #ifdef HAVE_SYS_SELECT_H 24 # include <sys/select.h> 25 #endif 26 # ifdef HAVE_UNISTD_H 27 #include <unistd.h> 28 #endif 29 #ifdef HAVE_ARPA_INET_H 30 # include <arpa/inet.h> 31 #endif 32 #ifdef HAVE_SYS_TIME_H 33 # include <sys/time.h> 34 #endif 35 36 #include <sys/types.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <stdio.h> 40 #include <ctype.h> 41 #include <time.h> 42 43 static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 44 { 45 struct timeval timeout; 46 int rc; 47 fd_set fd; 48 fd_set *writefd = NULL; 49 fd_set *readfd = NULL; 50 int dir; 51 52 timeout.tv_sec = 10; 53 timeout.tv_usec = 0; 54 55 FD_ZERO(&fd); 56 57 FD_SET(socket_fd, &fd); 58 59 /* now make sure we wait in the correct direction */ 60 dir = libssh2_session_block_directions(session); 61 62 if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 63 readfd = &fd; 64 65 if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 66 writefd = &fd; 67 68 rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 69 70 return rc; 71 } 72 73 int main(int argc, char *argv[]) 74 { 75 unsigned long hostaddr; 76 int sock, i, auth_pw = 1; 77 struct sockaddr_in sin; 78 const char *fingerprint; 79 LIBSSH2_SESSION *session; 80 const char *username="username"; 81 const char *password="password"; 82 const char *loclfile="sftp_write_nonblock.c"; 83 const char *sftppath="/tmp/sftp_write_nonblock.c"; 84 int rc; 85 FILE *local; 86 LIBSSH2_SFTP *sftp_session; 87 LIBSSH2_SFTP_HANDLE *sftp_handle; 88 char mem[1024 * 100]; 89 size_t nread; 90 char *ptr; 91 time_t start; 92 long total = 0; 93 int duration; 94 95 #ifdef WIN32 96 WSADATA wsadata; 97 int err; 98 99 err = WSAStartup(MAKEWORD(2,0), &wsadata); 100 if (err != 0) { 101 fprintf(stderr, "WSAStartup failed with error: %d\n", err); 102 return 1; 103 } 104 #endif 105 106 if (argc > 1) { 107 hostaddr = inet_addr(argv[1]); 108 } else { 109 hostaddr = htonl(0x7F000001); 110 } 111 112 if (argc > 2) { 113 username = argv[2]; 114 } 115 if (argc > 3) { 116 password = argv[3]; 117 } 118 if (argc > 4) { 119 loclfile = argv[4]; 120 } 121 if (argc > 5) { 122 sftppath = argv[5]; 123 } 124 125 rc = libssh2_init (0); 126 if (rc != 0) { 127 fprintf (stderr, "libssh2 initialization failed (%d)\n", rc); 128 return 1; 129 } 130 131 local = fopen(loclfile, "rb"); 132 if (!local) { 133 fprintf(stderr, "Can't open local file %s\n", loclfile); 134 return -1; 135 } 136 137 /* 138 * The application code is responsible for creating the socket 139 * and establishing the connection 140 */ 141 sock = socket(AF_INET, SOCK_STREAM, 0); 142 143 sin.sin_family = AF_INET; 144 sin.sin_port = htons(22); 145 sin.sin_addr.s_addr = hostaddr; 146 if (connect(sock, (struct sockaddr*)(&sin), 147 sizeof(struct sockaddr_in)) != 0) { 148 fprintf(stderr, "failed to connect!\n"); 149 return -1; 150 } 151 152 /* Create a session instance */ 153 session = libssh2_session_init(); 154 if (!session) 155 return -1; 156 157 /* Since we have set non-blocking, tell libssh2 we are non-blocking */ 158 libssh2_session_set_blocking(session, 0); 159 160 /* ... start it up. This will trade welcome banners, exchange keys, 161 * and setup crypto, compression, and MAC layers 162 */ 163 while ((rc = libssh2_session_handshake(session, sock)) 164 == LIBSSH2_ERROR_EAGAIN); 165 if (rc) { 166 fprintf(stderr, "Failure establishing SSH session: %d\n", rc); 167 return -1; 168 } 169 170 /* At this point we havn't yet authenticated. The first thing to do is 171 * check the hostkey's fingerprint against our known hosts Your app may 172 * have it hard coded, may go to a file, may present it to the user, 173 * that's your call 174 */ 175 fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); 176 fprintf(stderr, "Fingerprint: "); 177 for(i = 0; i < 20; i++) { 178 fprintf(stderr, "%02X ", (unsigned char)fingerprint[i]); 179 } 180 fprintf(stderr, "\n"); 181 182 if (auth_pw) { 183 /* We could authenticate via password */ 184 while ((rc = libssh2_userauth_password(session, username, password)) == 185 LIBSSH2_ERROR_EAGAIN); 186 if (rc) { 187 fprintf(stderr, "Authentication by password failed.\n"); 188 goto shutdown; 189 } 190 } else { 191 /* Or by public key */ 192 while ((rc = libssh2_userauth_publickey_fromfile(session, username, 193 "/home/username/.ssh/id_rsa.pub", 194 "/home/username/.ssh/id_rsa", 195 password)) == 196 LIBSSH2_ERROR_EAGAIN); 197 if (rc) { 198 fprintf(stderr, "\tAuthentication by public key failed\n"); 199 goto shutdown; 200 } 201 } 202 203 fprintf(stderr, "libssh2_sftp_init()!\n"); 204 do { 205 sftp_session = libssh2_sftp_init(session); 206 207 if (!sftp_session && 208 (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 209 fprintf(stderr, "Unable to init SFTP session\n"); 210 goto shutdown; 211 } 212 } while (!sftp_session); 213 214 fprintf(stderr, "libssh2_sftp_open()!\n"); 215 /* Request a file via SFTP */ 216 do { 217 sftp_handle = 218 libssh2_sftp_open(sftp_session, sftppath, 219 LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC, 220 LIBSSH2_SFTP_S_IRUSR|LIBSSH2_SFTP_S_IWUSR| 221 LIBSSH2_SFTP_S_IRGRP|LIBSSH2_SFTP_S_IROTH); 222 223 if (!sftp_handle && 224 (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN)) { 225 fprintf(stderr, "Unable to open file with SFTP\n"); 226 goto shutdown; 227 } 228 } while (!sftp_handle); 229 230 fprintf(stderr, "libssh2_sftp_open() is done, now send data!\n"); 231 232 start = time(NULL); 233 234 do { 235 nread = fread(mem, 1, sizeof(mem), local); 236 if (nread <= 0) { 237 /* end of file */ 238 break; 239 } 240 ptr = mem; 241 242 total += nread; 243 244 do { 245 /* write data in a loop until we block */ 246 while ((rc = libssh2_sftp_write(sftp_handle, ptr, nread)) == 247 LIBSSH2_ERROR_EAGAIN) { 248 waitsocket(sock, session); 249 } 250 if(rc < 0) 251 break; 252 ptr += rc; 253 nread -= rc; 254 255 } while (nread); 256 } while (rc > 0); 257 258 duration = (int)(time(NULL)-start); 259 260 fprintf(stderr, "%ld bytes in %d seconds makes %.1f bytes/sec\n", 261 total, duration, total/(double)duration); 262 263 264 fclose(local); 265 libssh2_sftp_close(sftp_handle); 266 libssh2_sftp_shutdown(sftp_session); 267 268 shutdown: 269 270 while (libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing") 271 == LIBSSH2_ERROR_EAGAIN); 272 libssh2_session_free(session); 273 274 #ifdef WIN32 275 closesocket(sock); 276 #else 277 close(sock); 278 #endif 279 fprintf(stderr, "all done\n"); 280 281 libssh2_exit(); 282 283 return 0; 284 }