Socket網絡編程--FTP客戶端(1)(Windows)


  已經好久沒有寫過博客進行分享了。具體原因,在以后說。

  這幾天在了解FTP協議,准備任務是寫一個FTP客戶端程序。直接上干貨了。

0.了解FTP作用

  就是一個提供一個文件的共享協議。

1.了解FTP協議

  FTP有指令和響應碼。FTP 控制幀即指 TELNET 交換信息,包含 TELNET 命令和選項。然而,大多數 FTP 控制幀是簡單的 ASCII 文本,可以分為 FTP 命令或 FTP 消息。 FTP 消息是對 FTP 命令的響應,它由帶有解釋文本的應答代碼構成。

  像這種利用交換信息來進行簡單的控制,這種協議,還真的很好玩的說。 命令與響應碼部分信息如下

  

  

2. 安裝一個FTP服務器

  我們先安裝一個FTP服務器,用於測試,這里是用FileZilla Server作為FTP服務器。

  啟動后,增加一個用戶user/user

3.FTP客戶端源代碼講解

  下面這個是FTPAPI.h文件

 1 #ifndef FTPAPI_H_INCLUDED
 2 #define FTPAPI_H_INCLUDED
 3 
 4 #include <stdio.h>
 5 #include <winsock2.h>
 6 
 7 SOCKET socket_connect(char *host, int port);
 8 SOCKET connect_server(char *host, int port);
 9 int ftp_sendcmd_re(SOCKET sock, char *cmd, char *re_buf, ssize_t *len);
10 int ftp_sendcmd(SOCKET sock, char *cmd);
11 int login_server(SOCKET sock, char *user, char *pwd);
12 void socket_close(int c_sock);
13 
14 
15 /**********可用命令*********/
16 SOCKET ftp_connect(char *host, int port, char *user, char *pwd); //連接到服務器
17 int ftp_quit(SOCKET sock); //斷開連接
18 int ftp_type(SOCKET sock, char mode); //設置FTP傳輸類型
19 int ftp_cwd(SOCKET sock, char *path); //更改工作目錄
20 int ftp_cdup(SOCKET sock); //回到上級目錄
21 int ftp_mkd(SOCKET sock, char *path); //創建目錄
22 SOCKET ftp_pasv_connect(SOCKET c_sock); //連接到PASV接口
23 int ftp_list(SOCKET c_sock, char *path, char **data, int *data_len); //列出FTP工作空間的所有目錄
24 int ftp_deletefolder(SOCKET sock, char *path); //刪除目錄
25 int ftp_deletefile(SOCKET sock, char *filename); //刪除文件
26 int ftp_renamefile(SOCKET sock, char *s, char *d); //修改文件/目錄&移動文件/目錄
27 int ftp_server2local(SOCKET c_sock, char *s, char *d, int * size); //從服務器復制文件到本地 RETR
28 int ftp_local2server(SOCKET c_sock, char *s, char *d, int * size); //從本地復制文件到服務器 STOR
29 int ftp_recv(SOCKET sock, char *re_buf, ssize_t *len); //獲取響應碼
30 
31 
32 #endif // FTPAPI_H_INCLUDED
View Code

   下面這個是FTPResponseCode.h 文件 是對應答碼簡單的描述

 1 #ifndef FTPRESPONSECODE_H_INCLUDED
 2 #define FTPRESPONSECODE_H_INCLUDED
 3 
 4 
 5 #define FTP_SUCCESS 200  //成功
 6 #define FTP_SERVICE_READY 220 //服務器就緒
 7 #define FTP_LOGIN_SUCCESS 230 //登錄因特網服務器
 8 #define FTP_FILE_ACTION_COMPLETE 250 //文件行為完成
 9 #define FTP_FILE_CREATED 257 //文件創建成功
10 #define FTP_PASSWORD_REQUIREd 331 //要求密碼
11 #define FTP_LOGIN_PASSWORD_INCORRECT 530 //用戶密碼錯誤
12 
13 
14 #endif // FTPRESPONSECODE_H_INCLUDED
View Code

  下面這些是FTPAPI.cpp文件的函數代碼

  創建一個socket連接並返回socket套接字 socket_connect

 1 /**
 2  * 作用: 創建一個Socket並返回.
 3  * 參數: IP或域名, 端口
 4  * 返回值: Socket套接字
 5  * */
 6 SOCKET socket_connect(char *host, int port)
 7 {
 8     int i=0;
 9     //初始化 Socket dll
10     WSADATA wsaData;
11     WORD socketVersion = MAKEWORD(2,0);
12     if(WSAStartup(socketVersion, &wsaData))
13     {
14         printf("Init socket dll error!");
15         exit(1);
16     }
17 
18     struct hostent * server = gethostbyname(host);
19     if(!server)
20         return -1;
21     unsigned char ch[4];
22     char ip[20];
23     //一個hostname 可以對應多個ip
24     while(server->h_addr_list[i]!=NULL)
25     {
26         memcpy(&ch,server->h_addr_list[i],4);
27         sprintf(ip,"%d.%d.%d.%d",ch[0],ch[1],ch[2],ch[3]);
28         //printf("%s\n",ip);
29         i++;
30     }
31 
32     //創建Socket
33     SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //TCP socket
34     if(SOCKET_ERROR == s)
35     {
36         printf("Create Socket Error!");
37         exit(1);
38     }
39     //設置超時連接
40     int timeout = 3000; //復雜的網絡環境要設置超時判斷
41     int ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout));
42     ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
43     //指定服務器地址
44     struct sockaddr_in address;
45     address.sin_family = AF_INET;
46     address.sin_addr.S_un.S_addr = inet_addr(ip);
47     address.sin_port = htons((unsigned short)port);
48     //連接
49     if(SOCKET_ERROR == connect(s,(LPSOCKADDR)&address,sizeof(address)))
50     {
51         printf("Can Not Connect To Server IP!\n");
52         exit(1);
53     }
54     return s;
55 }
View Code

  連接到一個ftp服務器 connect_server

 1 /**
 2  * 作用: 連接到一個FTP服務器,返回socket
 3  * 參數: IP或域名, 端口
 4  * 返回值: Socket套接字
 5  * */
 6 SOCKET connect_server(char *host, int port)
 7 {
 8     SOCKET ctrl_sock;
 9     char buf[BUFSIZE];
10     int result;
11     ssize_t len;
12 
13     ctrl_sock = socket_connect(host,port);
14     if(-1 == ctrl_sock)
15     {
16         return -1;
17     }
18     while((len = recv(ctrl_sock, buf, BUFSIZE, 0)) > 0)
19     {
20         //len = recv(ctrl_sock, buf, BUFSIZE, 0);
21         buf[len]=0;
22         printf("%s\n",buf); //220-FileZilla Server version 0.9.43 beta
23     }
24     sscanf(buf, "%d", &result);
25 
26     if(FTP_SERVICE_READY != result)
27     {
28         printf("FTP Not ready, Close the socet.");
29         closesocket(ctrl_sock); //關閉Socket
30         return -1;
31     }
32     return ctrl_sock;
33 }
View Code

  send發送命令,並返回recv結果 ftp_sendcmd_re

 1 /**
 2  * 作用: send發送命令,並返回recv結果
 3  * 參數: SOCKET,命令,命令返回碼-命令返回描述,命令返回字節數
 4  * 返回值: 0 表示發送成功  -1表示發送失敗
 5  * */
 6 int ftp_sendcmd_re(SOCKET sock, char *cmd, char *re_buf, ssize_t *len)
 7 {
 8     char buf[BUFSIZE];
 9     ssize_t r_len;
10     if(send(sock, cmd, strlen(cmd), 0) == -1)
11     {
12         return -1;
13     }
14     r_len = recv(sock, buf, BUFSIZE, 0);
15     if(r_len < 1)
16         return -1;
17     buf[r_len]=0;
18     if(NULL != len)
19         *len = r_len;
20     if(NULL != re_buf)
21         sprintf(re_buf, "%s", buf);
22     return 0;
23 }
View Code

  send發送命令 ftp_sendcmd

 1 /**
 2  * 作用: send發送命令
 3  * 參數: SOCKET,命令
 4  * 返回值: FTP響應碼
 5  * */
 6 int ftp_sendcmd(SOCKET sock, char *cmd)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     ssize_t len;
11     printf("FTP Client: %s", cmd);
12     result = ftp_sendcmd_re(sock, cmd, buf, &len);
13     printf("FTP Server: %s", buf);
14     if(0 == result)
15     {
16         sscanf(buf, "%d", &result);
17     }
18     return result;
19 }
View Code

  登錄FTP服務器 login_server

 1 /**
 2  * 作用: 登錄FTP服務器
 3  * 參數: SOCKET套接字,明文用戶名,明文密碼
 4  * 返回值: 0 表示登錄成功   -1 表示登錄失敗
 5  * */
 6 int login_server(SOCKET sock, char *user, char *pwd)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf, "USER %s\r\n", user);
11     //這里要對socket進行阻塞
12     int timeout=0;
13     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout));
14     result = ftp_sendcmd(sock, buf);
15     if(FTP_LOGIN_SUCCESS == result) //直接登錄
16         return 0;
17     else if(FTP_PASSWORD_REQUIREd == result) //需要密碼
18     {
19         sprintf(buf, "PASS %s\r\n", pwd);
20         result = ftp_sendcmd(sock, buf);
21         if(FTP_LOGIN_SUCCESS == result)
22         {
23             return 0;
24         }
25         else //530 密碼錯誤
26         {
27             return -1;
28         }
29     }
30     else
31     {
32         return -1;
33     }
34 }
View Code

  winsock使用后,要調用WSACleanup函數關閉網絡設備 socket_close

1 /**
2  * 作用: winsock使用后,要調用WSACleanup函數關閉網絡設備,以便釋放其占用的資源
3  * 參數: SOCKET
4  * 返回值: 無
5  * */
6 void socket_close(int c_sock)
7 {
8     WSACleanup();
9 }
View Code

  連接到FTP服務器 ftp_connect

 1 /**
 2  * 作用: 連接到FTP服務器
 3  * 參數: hostname或IP,端口,用戶名,密碼
 4  * 返回值: 已連接到FTP服務器的SOCKET   -1 表示登錄失敗
 5  * */
 6 SOCKET ftp_connect(char *host, int port, char *user, char *pwd)
 7 {
 8     SOCKET sock;
 9     sock = connect_server(host, port);
10     if(-1 == sock)
11     {
12         return -1;
13     }
14     if(-1 == login_server(sock, user, pwd))
15     {
16         closesocket(sock);
17         return -1;
18     }
19     return sock;
20 }
View Code

  斷開FTP服務器 ftp_quit

 1 /**
 2  * 作用: 斷開FTP服務器
 3  * 參數: SOCKET
 4  * 返回值: 成功斷開狀態碼
 5  * */
 6 int ftp_quit(SOCKET sock)
 7 {
 8     int result = 0;
 9     result = ftp_sendcmd(sock, "QUIT\r\n");
10     closesocket(sock);
11     socket_close(sock);
12     return result;
13 }
View Code

  設置FTP傳輸類型 A:ascii I:Binary  ftp_type

 1 /**
 2  * 作用: 設置FTP傳輸類型 A:ascii I:Binary
 3  * 參數: SOCkET,類型
 4  * 返回值: 0 表示成功   -1 表示失敗
 5  * */
 6 int ftp_type(SOCKET sock, char mode)
 7 {
 8     char buf[BUFSIZ];
 9     sprintf(buf,"TYPE %c\r\n", mode);
10     if(FTP_SUCCESS != ftp_sendcmd(sock, buf))
11         return -1;
12     else
13         return 0;
14 }
View Code

  更改工作目錄 ftp_cwd

 1 /**
 2  * 作用: 更改工作目錄
 3  * 參數: SOCKET,工作目錄
 4  * 返回值: 0 表示成功  -1 表示失敗
 5  * */
 6 int ftp_cwd(SOCKET sock, char *path)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf, "CWD %s\r\n", path);
11     result = ftp_sendcmd(sock, buf);
12     if(FTP_FILE_ACTION_COMPLETE != result)  //250 文件行為完成
13         return -1;
14     else
15         return 0;
16 }
View Code

  回到上級目錄 ftp_cdup

 1 /**
 2  * 作用: 回到上級目錄
 3  * 參數: SOCKET
 4  * 返回值: 0 正常操作返回  result 服務器返回響應碼
 5  * */
 6 int ftp_cdup(SOCKET sock)
 7 {
 8     int result;
 9     result = ftp_sendcmd(sock, "CDUP\r\n");
10     if(FTP_FILE_ACTION_COMPLETE == result || FTP_SUCCESS == result)
11         return 0;
12     else
13         return result;
14 }
View Code

  創建目錄 ftp_mkd

 1 /**
 2  * 作用: 創建目錄
 3  * 參數: SOCKET,文件目錄路徑(可相對路徑,絕對路徑)
 4  * 返回值: 0 正常操作返回  result 服務器返回響應碼
 5  * */
 6 int ftp_mkd(SOCKET sock, char *path)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf, "MKD %s\r\n", path);
11     result = ftp_sendcmd(sock, buf);
12     if(FTP_FILE_CREATED != result) //257 路徑名建立
13         return result; //550 目錄已存在
14     else
15         return 0;
16 }
View Code

  連接到PASV接口 ftp_pasv_connect

 1 /**
 2  * 作用: 連接到PASV接口
 3  *       PASV(被動)方式的連接過程是:
 4  *       客戶端向服務器的FTP端口(默認是21)發送連接請求,
 5  *       服務器接受連接,建立一條命令鏈路。
 6  * 參數: 命令鏈路SOCKET cmd-socket
 7  * 返回值: 數據鏈路SOCKET raw-socket  -1 表示創建失敗
 8  * */
 9 SOCKET ftp_pasv_connect(SOCKET c_sock)
10 {
11     SOCKET r_sock;
12     int send_result;
13     ssize_t len;
14     int addr[6]; //IP*4+Port*2
15     char buf[BUFSIZE];
16     char result_buf[BUFSIZE];
17 
18     //設置PASV被動模式
19     memset(buf,sizeof(buf),0);
20     sprintf(buf, "PASV\r\n");
21     send_result = ftp_sendcmd_re(c_sock, buf, result_buf, &len);
22     if(send_result == 0)
23     {
24         sscanf(result_buf, "%*[^(](%d,%d,%d,%d,%d,%d)",
25                &addr[0],&addr[1],&addr[2],&addr[3],
26                &addr[4],&addr[5]);
27     }
28 
29     //連接PASV端口
30     memset(buf, sizeof(buf), 0);
31     sprintf(buf, "%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]);
32     r_sock = socket_connect(buf,addr[4]*256+addr[5]);
33     if(-1 == r_sock)
34         return -1;
35     return r_sock;
36 }
View Code

  列出FTP工作空間的所有目錄 ftp_list

 1 /**
 2  * 作用: 列出FTP工作空間的所有目錄
 3  * 參數: 命令鏈路SOCKET,工作空間,列表信息,列表信息大小
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼  -1 表示創建pasv錯誤
 5  * */
 6 int ftp_list(SOCKET c_sock, char *path, char **data, int *data_len)
 7 {
 8     SOCKET r_sock;
 9     char buf[BUFSIZE];
10     int send_re;
11     int result;
12     ssize_t len,buf_len,total_len;
13 
14     //連接到PASV接口
15     r_sock = ftp_pasv_connect(c_sock);
16     if(-1 == r_sock)
17     {
18         return -1;
19     }
20     //發送LIST命令
21     memset(buf,sizeof(buf),0);
22     sprintf(buf, "LIST %s\r\n", path);
23     send_re = ftp_sendcmd(c_sock, buf);
24     if(send_re >= 300 || send_re == 0)
25         return send_re;
26     len=total_len=0;
27     buf_len=BUFSIZE;
28     char *re_buf = (char *)malloc(buf_len);
29     while( (len = recv(r_sock,buf,BUFSIZE,0)) > 0)
30     {
31         if(total_len+len > buf_len)
32         {
33             buf_len *= 2;
34             char *re_buf_n = (char *)malloc(buf_len);
35             memcpy(re_buf_n, re_buf, total_len);
36             free(re_buf);
37             re_buf = re_buf_n;
38         }
39         memcpy(re_buf+total_len, buf, len);
40         total_len += len;
41     }
42     closesocket(r_sock);
43 
44     //向服務器接收返回值
45     memset(buf, sizeof(buf), 0);
46     len = recv(c_sock, buf, BUFSIZE, 0);
47     buf[len] = 0;
48     sscanf(buf, "%d", &result);
49     if(result != 226)
50     {
51         free(re_buf);
52         return result;
53     }
54     *data = re_buf;
55     *data_len = total_len;
56     return 0;
57 }
View Code

  刪除目錄 ftp_deletefolder

 1 /**
 2  * 作用: 刪除目錄
 3  * 參數: 命令鏈路SOCKET,路徑目錄
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼
 5  * */
 6 int ftp_deletefolder(SOCKET sock, char *path)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf,"RMD %s\r\n", path);
11     result = ftp_sendcmd(sock, buf);
12     if(FTP_FILE_ACTION_COMPLETE != result)
13     {
14         //550 Directory not empty.
15         //550 Directory not found.
16         return result;
17     }
18     return 0;
19 }
View Code

  刪除文件 ftp_deletefile

 1 /**
 2  * 作用: 刪除文件
 3  * 參數: 命令鏈路SOCKET,路徑文件(相對/絕對)
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼
 5  * */
 6 int ftp_deletefile(SOCKET sock, char *filename)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf, "DELE %s\r\n", filename);
11     result = ftp_sendcmd(sock, buf);
12     if(FTP_FILE_ACTION_COMPLETE != 250) //250 File deleted successfully
13     {
14         //550 File not found.
15         return result;
16     }
17     return 0;
18 }
View Code

  修改文件名&移動目錄 ftp_renamefile

 1 /**
 2  * 作用: 修改文件名&移動目錄
 3  * 參數: 命令鏈路SOCKET,源地址,目的地址
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼
 5  * */
 6 int ftp_renamefile(SOCKET sock, char *s, char *d)
 7 {
 8     char buf[BUFSIZE];
 9     int result;
10     sprintf(buf, "RNFR %s\r\n", s);
11     result = ftp_sendcmd(sock, buf);
12     if(350 != result) //350 文件行為暫停,因為要進行移動操作
13         return result;
14     sprintf(buf, "RNTO %s\r\n", d);
15     result = ftp_sendcmd(sock, buf);
16     if(FTP_FILE_ACTION_COMPLETE != result)
17     {
18         return result;
19     }
20     return 0;
21 }
View Code

  從服務器復制文件到本地 RETR  ftp_server2local

 1 /**
 2  * 作用: 從服務器復制文件到本地 RETR
 3  * 參數: SOCKET,源地址,目的地址,文件大小
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼
 5  *         -1:文件創建失敗  -2 pasv接口錯誤
 6  * */
 7 int ftp_server2local(SOCKET c_sock, char *s, char *d, int * size)
 8 {
 9     SOCKET d_sock;
10     ssize_t len,write_len;
11     char buf[BUFSIZ];
12     int result;
13     *size=0;
14     //打開本地文件
15     FILE * fp = fopen(d, "wb");
16     if(NULL == fp)
17     {
18         printf("Can't Open the file.\n");
19         return -1;
20     }
21     //設置傳輸模式
22     ftp_type(c_sock,'I');
23 
24     //連接到PASV接口 用於傳輸文件
25     d_sock = ftp_pasv_connect(c_sock);
26     if(-1 == d_sock)
27     {
28         fclose(fp); //關閉文件
29         return -2;
30     }
31 
32     //發送RETR命令
33     memset(buf, sizeof(buf), 0);
34     sprintf(buf, "RETR %s\r\n", s);
35     result = ftp_sendcmd(c_sock, buf);
36     // 150 Opening data channel for file download from server of "xxxx"
37     if(result >= 300 || result == 0) //失敗可能是沒有權限什么的,具體看響應碼
38     {
39         fclose(fp);
40         return result;
41     }
42 
43     //開始向PASV讀取數據(下載)
44     memset(buf, sizeof(buf), 0);
45     while((len = recv(d_sock, buf, BUFSIZE, 0)) > 0 )
46     {
47         write_len = fwrite(&buf, len, 1, fp);
48         if(write_len != 1) //寫入文件不完整
49         {
50             closesocket(d_sock); //關閉套接字
51             fclose(fp); //關閉文件
52             return -1;
53         }
54         if(NULL != size)
55         {
56             *size += write_len;
57         }
58     }
59     //下載完成
60     closesocket(d_sock);
61     fclose(fp);
62 
63     //向服務器接收返回值
64     memset(buf, sizeof(buf), 0);
65     len = recv(c_sock, buf, BUFSIZE, 0);
66     buf[len] = 0;
67     printf("%s\n",buf);
68     sscanf(buf, "%d", &result);
69     if(result >= 300)
70     {
71         return result;
72     }
73     //226 Successfully transferred "xxxx"
74     return 0;
75 }
View Code

  從本地復制文件到服務器 STOR ftp_local2server

 1 /**
 2  * 作用: 從本地復制文件到服務器 STOR
 3  * 參數: SOCKET,源地址,目的地址,文件大小
 4  * 返回值: 0 表示列表成功  result>0 表示其他錯誤響應碼
 5  *         -1:文件創建失敗  -2 pasv接口錯誤
 6  * */
 7 int ftp_local2server(SOCKET c_sock, char *s, char *d, int * size)
 8 {
 9     SOCKET d_sock;
10     ssize_t len,send_len;
11     char buf[BUFSIZE];
12     FILE * fp;
13     int send_re;
14     int result;
15     //打開本地文件
16     fp = fopen(s, "rb");
17     if(NULL == fp)
18     {
19         printf("Can't Not Open the file.\n");
20         return -1;
21     }
22     //設置傳輸模式
23     ftp_type(c_sock, 'I');
24     //連接到PASV接口
25     d_sock = ftp_pasv_connect(c_sock);
26     if(d_sock == -1)
27     {
28         fclose(fp);
29         return -1;
30     }
31 
32     //發送STOR命令
33     memset(buf, sizeof(buf), 0);
34     sprintf(buf, "STOR %s\r\n", d);
35     send_re = ftp_sendcmd(c_sock, buf);
36     if(send_re >= 300 || send_re == 0)
37     {
38         fclose(fp);
39         return send_re;
40     }
41 
42     //開始向PASV通道寫數據
43     memset(buf, sizeof(buf), 0);
44     while( (len = fread(buf, 1, BUFSIZE, fp)) > 0)
45     {
46         send_len = send(d_sock, buf, len, 0);
47         if(send_len != len)
48         {
49             closesocket(d_sock);
50             fclose(fp);
51             return -1;
52         }
53         if(NULL != size)
54         {
55             *size += send_len;
56         }
57     }
58     //完成上傳
59     closesocket(d_sock);
60     fclose(fp);
61 
62     //向服務器接收響應碼
63     memset(buf, sizeof(buf), 0);
64     len = recv(c_sock, buf, BUFSIZE, 0);
65     buf[len] = 0;
66     sscanf(buf, "%d", &result);
67     if(result >= 300)
68     {
69         return result;
70     }
71     return 0;
72 }
View Code

  獲取一行響應碼 ftp_recv

 1 /**
 2  * 作用: 獲取一行響應碼
 3  * 參數:
 4  * 返回值:
 5  * */
 6 int ftp_recv(SOCKET sock, char *re_buf, ssize_t *len)
 7 {
 8     char buf[BUFSIZE];
 9     ssize_t r_len;
10     int timeout = 3000;
11     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
12     r_len = recv(sock, buf, BUFSIZE, 0);
13     timeout = 0;
14     setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
15     if(r_len < 1)
16         return -1;
17     buf[r_len]=0;
18     if(NULL != len)
19         *len = r_len;
20     if(NULL != re_buf)
21         sprintf(re_buf, "%s", buf);
22     return 0;
23 }
View Code

 

 4.測試ftp客戶端

  下載文件到本地

1     SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登錄到FTP服務器
2     int ret = ftp_server2local(s,"user/user.zip","bin.zip",&size); //在FTP服務器獲取文件
3     ftp_quit(s); //退出FTP服務器

  上傳文件到服務器

1     SOCKET s = ftp_connect("127.0.0.1",21,"user","user"); //登錄到FTP服務器
2     int ret = ftp_local2server(s,"user/user.zip","bin.zip",&size); //發送文件到FTP服務器
3     ftp_quit(s); //退出FTP服務器

  下面這個是服務器的日志信息

  

  下面這個是程序打印的調試信息

   

 

5.后話

  到這里這個簡單的ftp庫就可以實現絕大部分的客戶端功能了,但是這里面有一個問題,就是ftp是明文傳輸用戶名/密碼的,如果ftp上的文件比較重要的話,那么就有點問題了。當然這個不是本次的關注點,本次主要是了解ftp協議,還有從代碼中了解這種交換控制命令的方法是一種很不錯的技術手段,雖然這種方法已經是好多年前的,不安全,也過時了。但還是有可學的地方。

6.附錄

  下面這個附錄是利用wireshark進行本地網絡抓包測試。

1、抓包,要看部署點,在路由器、交換機等設備上做端口鏡像、或分光口,或是接HUB、TAP等設備就可以直接獲得通過這些口的報文。
2、抓包,也可在以局域網部署相關的網管軟件或黑客工具(比如cain),可以用arp騙方式,讓你的數據先發送到監控機上,然后再轉發走。。這樣你的數據就。。

建議:
1、建議在電腦上打開ARP防護功能
2、在使用中盡量使用加密傳輸的工具,比如SSH、SSL、QQ一類的東西。可避免一些危害.

  注意wireshark是不能抓取本地回環地址的數據包的,所以我以遠程ftp服務器進行測試

   

  這里是通過瀏覽器進行連接的。wireshark 1.12.4 從上面可以看到的信息 29-44這些表示了,瀏覽器一開始使用匿名進行登錄,發現登錄不上,所以請求用戶名登錄在81 82 84 85這4行中我們可以分析到,我是輸入用戶名user 密碼user進行登錄的,第106行表示用戶名/密碼錯誤。 如果是230 Login in 就表示成功登錄了。如果我們捉到了這些信息,那么我們就可以進行登錄了。這樣就不安全了。既然ftp這么不安全為什么那么多地方用到ftp共享文件。這個就要說到ftp的作用了,ftp作用本來就是共享文件,所以安全性就不是很重要了。 至於加密方式以后再講。

   (開發環境mignw 編譯的時候要加入libws2_32.a 這個庫, 編譯命令 g++ ftpapi.cpp -c -o ftpapi.o -lws2_32)

 

 

  參考資料

  TanHao的 THFTPAPI.c 文件 http://www.tanhao.me

  文件下載 ftpapi.zip http://files.cnblogs.com/files/wunaozai/ftpapi20150512.zip 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM