Socket網絡編程--FTP客戶端

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

  已經很久沒有寫過博客進行分享了。具體緣由,在之後說。html

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

0.瞭解FTP做用api

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

1.瞭解FTP協議安全

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

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

  

  

2. 安裝一個FTP服務器socket

  咱們先安裝一個FTP服務器,用於測試,這裏是用FileZilla Server做爲FTP服務器。ide

  啓動後,增長一個用戶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
複製代碼

   下面這個是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
複製代碼

  下面這些是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 }
複製代碼

  鏈接到一個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 }
複製代碼

  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 }
複製代碼

  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 }
複製代碼

  登陸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 }
複製代碼

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

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

  鏈接到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 }
複製代碼

  斷開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 }
複製代碼

  設置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 }
複製代碼

  更改工做目錄 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 }
複製代碼

  回到上級目錄 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 }
複製代碼

  建立目錄 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 }
複製代碼

  鏈接到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 }
複製代碼

  列出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 }
複製代碼

  刪除目錄 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 }
複製代碼

  刪除文件 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 }
複製代碼

  修改文件名&移動目錄 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 }
複製代碼

  從服務器複製文件到本地 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 }
複製代碼

  從本地複製文件到服務器 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 }
複製代碼

  獲取一行響應碼 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 }
複製代碼

 

 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進行本地網絡抓包測試。

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

建議:
一、建議在電腦上打開ARP防禦功能
二、在使用中儘可能使用加密傳輸的工具,好比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 

做者:無腦仔的小明
出處:http://www.cnblogs.com/wunaozai/ 本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利。 若是文中有什麼錯誤,歡迎指出。以避免更多的人被誤導。
相關文章
相關標籤/搜索