FTP 客户端C实现
使用 Socket 通信實現 FTP 客戶端程序
FTP 概述
文件傳輸協議(FTP)作為網絡共享文件的傳輸協議,在網絡應用軟件中具有廣泛的應用。FTP的目標是提高文件的共享性和可靠高效地傳送數據。
在傳輸文件時,FTP 客戶端程序先與服務器建立連接,然后向服務器發送命令。服務器收到命令后給予響應,并執行命令。FTP 協議與操作系統無關,任何操作系統上的程序只要符合 FTP 協議,就可以相互傳輸數據。本文主要基于 LINUX 平臺,對 FTP 客戶端的實現原理進行詳盡的解釋并闡述如何使用 C 語言編寫一個簡單的 FTP 客戶端。
FTP 協議
相比其他協議,如 HTTP 協議,FTP 協議要復雜一些。與一般的 C/S 應用不同點在于一般的C/S 應用程序一般只會建立一個 Socket 連接,這個連接同時處理服務器端和客戶端的連接命令和數據傳輸。而FTP協議中將命令與數據分開傳送的方法提高了效率。
FTP 使用 2 個端口,一個數據端口和一個命令端口(也叫做控制端口)。這兩個端口一般是21 (命令端口)和 20 (數據端口)。控制 Socket 用來傳送命令,數據 Socket 是用于傳送數據。每一個 FTP 命令發送之后,FTP 服務器都會返回一個字符串,其中包括一個響應代碼和一些說明信息。其中的返回碼主要是用于判斷命令是否被成功執行了。
命令端口
一般來說,客戶端有一個 Socket 用來連接 FTP 服務器的相關端口,它負責 FTP 命令的發送和接收返回的響應信息。一些操作如“登錄”、“改變目錄”、“刪除文件”,依靠這個連接發送命令就可完成。
數據端口
對于有數據傳輸的操作,主要是顯示目錄列表,上傳、下載文件,我們需要依靠另一個 Socket來完成。
如果使用被動模式,通常服務器端會返回一個端口號。客戶端需要用另開一個 Socket 來連接這個端口,然后我們可根據操作來發送命令,數據會通過新開的一個端口傳輸。
如果使用主動模式,通常客戶端會發送一個端口號給服務器端,并在這個端口監聽。服務器需要連接到客戶端開啟的這個數據端口,并進行數據的傳輸。
下面對 FTP 的主動模式和被動模式做一個簡單的介紹。
主動模式 (PORT)
主動模式下,客戶端隨機打開一個大于 1024 的端口向服務器的命令端口 P,即 21 端口,發起連接,同時開放N +1 端口監聽,并向服務器發出 “port N+1” 命令,由服務器從它自己的數據端口 (20) 主動連接到客戶端指定的數據端口 (N+1)。
FTP 的客戶端只是告訴服務器自己的端口號,讓服務器來連接客戶端指定的端口。對于客戶端的防火墻來說,這是從外部到內部的連接,可能會被阻塞。
被動模式 (PASV)
為了解決服務器發起到客戶的連接問題,有了另一種 FTP 連接方式,即被動方式。命令連接和數據連接都由客戶端發起,這樣就解決了從服務器到客戶端的數據端口的連接被防火墻過濾的問題。
被動模式下,當開啟一個 FTP 連接時,客戶端打開兩個任意的本地端口 (N > 1024 和 N+1) 。
第一個端口連接服務器的 21 端口,提交 PASV 命令。然后,服務器會開啟一個任意的端口 (P > 1024 ),返回如“227 entering passive mode (127,0,0,1,4,18)”。 它返回了 227 開頭的信息,在括號中有以逗號隔開的六個數字,前四個指服務器的地址,最后兩個,將倒數第二個乘 256 再加上最后一個數字,這就是 FTP 服務器開放的用來進行數據傳輸的端口。如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口號是 p1*256+p2,ip 地址為h1.h2.h3.h4。這意味著在服務器上有一個端口被開放。客戶端收到命令取得端口號之后, 會通過 N+1 號端口連接服務器的端口 P,然后在兩個端口之間進行數據傳輸。
主要用到的 FTP 命令
FTP 每個命令都有 3 到 4 個字母組成,命令后面跟參數,用空格分開。每個命令都以 "\r\n"結束。
要下載或上傳一個文件,首先要登入 FTP 服務器,然后發送命令,最后退出。這個過程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、PORT、QUIT。
USER: 指定用戶名。通常是控制連接后第一個發出的命令。“USER gaoleyi\r\n”: 用戶名為gaoleyi 登錄。
PASS: 指定用戶密碼。該命令緊跟 USER 命令后。“PASS gaoleyi\r\n”:密碼為 gaoleyi。
SIZE: 從服務器上返回指定文件的大小。“SIZE file.txt\r\n”:如果 file.txt 文件存在,則返回該文件的大小。
CWD: 改變工作目錄。如:“CWD dirname\r\n”。
PASV: 讓服務器在數據端口監聽,進入被動模式。如:“PASV\r\n”。
PORT: 告訴 FTP 服務器客戶端監聽的端口號,讓 FTP 服務器采用主動模式連接客戶端。如:“PORT h1,h2,h3,h4,p1,p2”。
RETR: 下載文件。“RETR file.txt \r\n”:下載文件 file.txt。
STOR: 上傳文件。“STOR file.txt\r\n”:上傳文件 file.txt。
REST: 該命令并不傳送文件,而是略過指定點后的數據。此命令后應該跟其它要求文件傳輸的 FTP 命令。“REST 100\r\n”:重新指定文件傳送的偏移量為 100 字節。
QUIT: 關閉與服務器的連接。
FTP 響應碼
客戶端發送 FTP 命令后,服務器返回響應碼。
響應碼用三位數字編碼表示:
第一個數字給出了命令狀態的一般性指示,比如響應成功、失敗或不完整。
第二個數字是響應類型的分類,如 2 代表跟連接有關的響應,3 代表用戶認證。
第三個數字提供了更加詳細的信息。
第一個數字的含義如下:
1 表示服務器正確接收信息,還未處理。
2 表示服務器已經正確處理信息。
3 表示服務器正確接收信息,正在處理。
4 表示信息暫時錯誤。
5 表示信息永久錯誤。
第二個數字的含義如下:
0 表示語法。
1 表示系統狀態和信息。
2 表示連接狀態。
3 表示與用戶認證有關的信息。
4 表示未定義。
5 表示與文件系統有關的信息。
Socket 編程的幾個重要步驟
Socket 客戶端編程主要步驟如下:
Socket 服務器端編程主要步驟如下:
實現 FTP 客戶端上傳下載功能
下面讓我們通過一個例子來對 FTP 客戶端有一個深入的了解。本文實現的 FTP 客戶端有下列功能:
本例中使用的 FTP 服務器為 filezilla。在整個交互的過程中,控制連接始終處于連接的狀態,數據連接在每傳輸一個文件時先打開,后關閉。
客戶端和 FTP 服務器建立 Socket 連接
當客戶端與服務器建立連接后,服務器會返回 220 的響應碼和一些歡迎信息。
圖 1. 客戶端連接到服務器端
清單 1. 客戶端連接到 FTP 服務器,接收歡迎信息
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | SOCKET control_sock; struct hostent *hp; struct sockaddr_in server; memset(&server, 0, sizeof(struct sockaddr_in)); ? /* 初始化socket */ control_sock = socket(AF_INET, SOCK_STREAM, 0); hp = gethostbyname(server_name); memcpy(&server.sin_addr, hp->h_addr, hp->h_length); server.sin_family = AF_INET; server.sin_port = htons(port); ? /* 連接到服務器端 */ connect(control_sock,(struct sockaddr *)&server, sizeof(server)); /* 客戶端接收服務器端的一些歡迎信息 */ read(control_sock, read_buf, read_len); |
客戶端登錄 FTP 服務器
當客戶端發送用戶名和密碼,服務器驗證通過后,會返回 230 的響應碼。然后客戶端就可以向服務器端發送命令了。
圖 2. 客戶端登錄 FTP 服務器
清單 2. 客戶端發送用戶名和密碼,登入 FTP 服務器
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | /* 命令 ”USER username\r\n” */ sprintf(send_buf,"USER %s\r\n",username); /*客戶端發送用戶名到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”331 User name okay, need password.” */ read(control_sock, read_buf, read_len); ? /* 命令 ”PASS password\r\n” */ sprintf(send_buf,"PASS %s\r\n",password); /* 客戶端發送密碼到服務器端 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”230 User logged in, proceed.” */ read(control_sock, read_buf, read_len); |
客戶端讓 FTP 服務器進入被動模式
當客戶端在下載/上傳文件前,要先發送命令讓服務器進入被動模式。服務器會打開數據端口并監聽。并返回響應碼 227 和數據連接的端口號。
圖 3. 客戶端讓服務器進入被動模式
清單 3. 讓服務器進入被動模式,在數據端口監聽
| 1 2 3 4 5 6 7 | /* 命令 ”PASV\r\n” */ sprintf(send_buf,"PASV\r\n"); /* 客戶端告訴服務器用被動模式 */ write(control_sock, send_buf, strlen(send_buf)); /*客戶端接收服務器的響應碼和新開的端口號, * 正常為 ”227 Entering passive mode (<h1,h2,h3,h4,p1,p2>)” */ read(control_sock, read_buf, read_len); |
客戶端通過被動模式下載文件
當客戶端發送命令下載文件。服務器會返回響應碼 150,并向數據連接發送文件內容。
圖 4. 客戶端從FTP服務器端下載文件
清單 4. 客戶端連接到 FTP 服務器的數據端口并下載文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /* 連接服務器新開的數據端口 */ connect(data_sock,(struct sockaddr *)&server, sizeof(server)); /* 命令 ”CWD dirname\r\n” */ sprintf(send_buf,"CWD %s\r\n", dirname); /* 客戶端發送命令改變工作目錄 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”250 Command okay.” */ read(control_sock, read_buf, read_len); ? /* 命令 ”SIZE filename\r\n” */ sprintf(send_buf,"SIZE %s\r\n",filename); /* 客戶端發送命令從服務器端得到下載文件的大小 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”213 <size>” */ read(control_sock, read_buf, read_len); ? /* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客戶端發送命令從服務器端下載文件 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”150 Opening data connection.” */ read(control_sock, read_buf, read_len); ? /* 客戶端創建文件 */ file_handle = open(disk_name, CRFLAGS, RWXALL); for( ; ; ) { ... ... /* 客戶端通過數據連接 從服務器接收文件內容 */ read(data_sock, read_buf, read_len); /* 客戶端寫文件 */ write(file_handle, read_buf, read_len); ... ... } /* 客戶端關閉文件 */ rc = close(file_handle); |
客戶端退出服務器
當客戶端下載完畢后,發送命令退出服務器,并關閉連接。服務器會返回響應碼 200。
圖 5. 客戶端從 FTP 服務器退出
清單 5. 客戶端關閉數據連接,退出 FTP 服務器并關閉控制連接
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | /* 客戶端關閉數據連接 */ close(data_sock); /* 客戶端接收服務器的響應碼和信息,正常為 ”226 Transfer complete.” */ read(control_sock, read_buf, read_len); ? /* 命令 ”QUIT\r\n” */ sprintf(send_buf,"QUIT\r\n"); /* 客戶端將斷開與服務器端的連接 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼,正常為 ”200 Closes connection.” */ read(control_sock, read_buf, read_len); /* 客戶端關閉控制連接 */ close(control_sock); |
至此,下載文件已經完成。需要注意的是發送 FTP 命令的時候,在命令后要緊跟 “\r\n”,否則服務器不會返回信息。回車換行符號 “\r\n” 是 FTP 命令的結尾符號,當服務器接收到這個符號時,認為客戶端發送的命令已經結束,開始處理。否則會繼續等待。
讓我們來看一下 FTP 服務器這一端的響應情況:
清單 6. 客戶端下載文件時,FTP 服務器的響應輸出
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | (not logged in) (127.0.0.1)> Connected, sending welcome message... (not logged in) (127.0.0.1)> 220-FileZilla Server version 0.9.36 beta (not logged in) (127.0.0.1)> 220 hello gaoleyi (not logged in) (127.0.0.1)> USER gaoleyi (not logged in) (127.0.0.1)> 331 Password required for gaoleyi (not logged in) (127.0.0.1)> PASS ********* gaoleyi (127.0.0.1)> 230 Logged on gaoleyi (127.0.0.1)> PWD gaoleyi (127.0.0.1)> 257 "/" is current directory. gaoleyi (127.0.0.1)> SIZE file.txt gaoleyi (127.0.0.1)> 213 4096 gaoleyi (127.0.0.1)> PASV gaoleyi (127.0.0.1)> 227 Entering Passive Mode (127,0,0,1,13,67) gaoleyi (127.0.0.1)> RETR file.txt gaoleyi (127.0.0.1)> 150 Connection accepted gaoleyi (127.0.0.1)> 226 Transfer OK gaoleyi (127.0.0.1)> QUIT gaoleyi (127.0.0.1)> 221 Goodbye |
首先,服務器準備就緒后返回 220。客戶端接收到服務器端返回的響應碼后,相繼發送“USER username” 和 “PASS password” 命令登錄。隨后,服務器返回的響應碼為 230 開頭,說明客戶端已經登入了。這時,客戶端發送 PASV 命令讓服務器進入被動模式。服務器返回如 “227 Entering Passive Mode (127,0,0,1,13,67)”,客戶端從中得到端口號,然后連接到服務器的數據端口。接下來,客戶端發送下載命令,服務器會返回響應碼 150,并從數據端口發送數據。最后,服務器返回 “226 transfer complete”,表明數據傳輸完成。
需要注意的是,客戶端不要一次發送多條命令,例如我們要打開一個目錄并且顯示這個目錄,我們得發送 CWD dirname,PASV,LIST。在發送完 CWD dirname 之后等待響應代碼,然后再發送后面一條。當 PASV 返回之后,我們打開另一個 Socket 連接到相關端口上。然后發送 LIST,返回 125 之后在開始接收數據,最后返回 226 表明完成。
在傳輸多個文件的過程中,需要注意的是每次新的傳輸都必須重新使用 PASV 獲取新的端口號,接收完數據后應該關閉該數據連接,這樣服務器才會返回一個 2XX 成功的響應。然后客戶端可以繼續下一個文件的傳輸。
上傳文件與下載文件相比,登入驗證和切換被動模式都如出一轍,只需要改變發送到服務器端的命令,并通過數據連接發送文件內容。
客戶端通過被動模式向服務器上傳文件
當客戶端發送命令上傳文件,服務器會從數據連接接收文件。
圖 6. 客戶端連接到 FTP 服務器的數據端口并上傳文件
客戶端通過主動模式向服務器上傳文件
到目前為止,本文介紹的都是客戶端用被動模式進行文件的上傳和下載。下面將介紹客戶端用主動模式下載文件。
圖 7. 用主動模式從 FTP 服務器下載文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | 清單 7. 用主動模式從 FTP 服務器下載文件的示例 C 程序 ... ... SOCKET data_sock; data_sock = socket(AF_INET, SOCK_STREAM, 0); struct? sockaddr_in? name; name.sin_family = AF_INET; name.sin_addr.s_addr = htons(INADDR_ANY); server_port = p1*256+p2; length = sizeof(name); name.sin_port = htons(server_port); bind(server_sock, (struct sockaddr *)&name, length); struct? sockaddr_in client_name; length = sizeof(client_name); ? /* 客戶端開始監聽端口p1*256+p2 */ listen(server_sock, 64); ? /* 命令 ”PORT \r\n” */ sprintf(send_buf,"PORT 1287,0,0,1,%d,%d\r\n", p1, p2); write(control_sock, send_buf,strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”200 Port command successful” */ read(control_sock, read_buf, read_len); ? sprintf(send_buf,"RETR filename.txt\r\n"); write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,正常為 ”150 Opening data channel for file transfer.” */ read(control_sock, read_buf, read_len); ? /* ftp客戶端接受服務器端的連接請求 */ data_sock = accept(server_sock,(struct sockaddr *)&client_name, &length); ... ... ? file_handle = open(disk_name, ROFLAGS, RWXALL); for( ; ; ) { ... ... read(data_sock, read_buf, read_len); write(file_handle, read_buf, read_len); ... ... } close(file_handle); |
客戶端通過 PORT 命令告訴服務器連接自己的 p1*256+p2 端口。隨后在這個端口進行監聽,等待 FTP 服務器連接上來, 再通過這個數據端口來傳輸文件。PORT 方式在傳送數據時,FTP 客戶端其實就相當于一個服務器端,由 FTP 服務器主動連接自己。
斷點續傳
由于網絡不穩定,在傳輸文件的過程中,可能會發生連接斷開的情況,這時候需要客戶端支持斷點續傳的功能,下次能夠從上次終止的地方開始接著傳送。需要使用命令 REST。如果在斷開連接前,一個文件已經傳輸了 512 個字節。則斷點續傳開始的位置為 512,服務器會跳過傳輸文件的前 512 字節。
清單 8. 從 FTP 服務器斷點續傳下載文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ... ... /* 命令 ”REST offset\r\n” */ sprintf(send_buf,"REST %ld\r\n", offset); /* 客戶端發送命令指定下載文件的偏移量 */ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息, *正常為 ”350 Restarting at <position>. Send STORE or RETRIEVE to initiate transfer.” */ read(control_sock, read_buf, read_len); ... ... ? /* 命令 ”RETR filename\r\n” */ sprintf(send_buf,"RETR %s\r\n",filename); /* 客戶端發送命令從服務器端下載文件, 并且跳過該文件的前offset字節*/ write(control_sock, send_buf, strlen(send_buf)); /* 客戶端接收服務器的響應碼和信息,* *正常為 ”150 Connection accepted, restarting at offset <position>” */ read(control_sock, read_buf, read_len); ... ... ? file_handle = open(disk_name, CRFLAGS, RWXALL); /* 指向文件寫入的初始位置 */ lseek(file_handle, offset, SEEK_SET); ... ... |
結束語
本文從應用實現的角度,介紹了 FTP 協議。并用詳盡的例子分析了如何用主動模式和被動模式實現 FTP 客戶端上傳下載文件,如何進行斷點續傳。通過本文可以讓讀者對 FTP 客戶端的原理有一個深入的了解。
這里借用輕飄飛揚博主的代碼測試通過
#include<stdio.h> #include<sys/socket.h> #include<stdlib.h> #include<string.h> #include<strings.h> #include<unistd.h> #include<netinet/in.h> #include<netdb.h> #include<errno.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #define SERV_PORT 21 #define MAXSIZE 1024 #define SA struct sockaddr static int control_sockfd; int npsupport; int login_yes; int f;//f=0時為默認文件結構 int login(); void ftp_list(int control_sockfd); void zeromery(char *a,int len); void ftp_pwd(int control_sockfd); void ftp_changdir(char dir[],int control_sockfd); void ftp_quit(int control_sockfd); void ftp_creat_mkd(char *path,int control_sockfd); void ftp_back(int control_sockfd); void ftp_stru(int control_sockfd); void ftp_rest(int control_sockfd); int ftp_download(int control_sockfd); char *itoa(int value, char *string, int radix); int main(int argc,char **argv) {printf("ftp>");char command[MAXSIZE];char*cmd;scanf("%s",command);cmd=command;while(*(cmd)==' ')cmd++;if(strncmp(cmd,"login",5)==0){login();if(login_yes==1){while(1){comm: sleep(1);printf("ftp>");zeromery(command,1024);scanf("%s",command);cmd=command;while(*(cmd)==' ')cmd++;if(strncmp(cmd,"pasv",4)==0){ftp_list(control_sockfd);}if(strncmp(cmd,"port",4)==0){ftp_list(control_sockfd);}if(strncmp(cmd,"list",4)==0){ftp_pwd(control_sockfd);ftp_list(control_sockfd);}if(strncmp(cmd,"pwd",3)==0){ftp_pwd(control_sockfd);}if(strncmp(cmd,"mkdir",5)==0){char path[60];zeromery(path,60);printf("創建的路徑名: ");scanf("%s",path);printf("s/n",path);ftp_creat_mkd(path,control_sockfd);}if(strncmp(cmd,"back",4)==0){ftp_back(control_sockfd);ftp_pwd(control_sockfd);}if(strncmp(cmd,"cd",2)==0){int i;char path[60];zeromery(path,60);printf("要到的路徑:");scanf("%s",path);printf("%s/n",path);ftp_changdir(path,control_sockfd);}if(strncmp(cmd,"get",3)==0){ftp_pwd(control_sockfd); ftp_download(control_sockfd);}if(strncmp(cmd,"up",3)==0){ftp_pwd(control_sockfd);ftp_up(control_sockfd); }if(strncmp(cmd,"quit",4)==0){printf("bye^_^/n");close(control_sockfd);break;}printf("支持 list,pwd,mkdir,back,cd,up,get/n"); }}else if(login_yes==0){ int i;//不成功登錄下最多還有兩次機會,如果不能在兩次登錄,則,關閉鏈接。printf("Can not login vsftpd");for(i=2;i>0;i--){printf("你還有 %d 登錄機會/n",i); login();if(login_yes==1){goto comm;}}if(i==0){printf("你不能在登錄!/n");close(control_sockfd);}}else if (strncmp(cmd,"quit",4)==0){ftp_quit(control_sockfd);close(control_sockfd);}}return 0; } int login() {//初始化端口信息struct sockaddr_in serv_addr;char senddate,recvdate;char sendline[MAXSIZE],recvline[MAXSIZE];struct hostent *host;//獲取hostent中相關參數char name[MAXSIZE],password[MAXSIZE];printf("please enter the hostname/n");printf("ftp-> ");scanf("%s",name);host=gethostbyname(name);if(host==NULL){printf("get host by name is error!/n");login_yes=0;}else{//創建socketcontrol_sockfd=socket(AF_INET,SOCK_STREAM,0);if(control_sockfd<0){printf("socket is error/n");login_yes=0;}//設置sockaddr_in 結構體中的相關參數bzero(&serv_addr,sizeof(serv_addr));serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(SERV_PORT);serv_addr.sin_addr.s_addr=INADDR_ANY;//調用connect函數發起連接char addr[MAXSIZE];if((connect(control_sockfd,(SA*)&serv_addr,sizeof(serv_addr)))<0){printf("connect is error/n");login_yes=0;}printf("connect to %s/n",inet_ntop(AF_INET,host->h_addr,addr,1024));recvdate=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvdate==-1){printf("recvdate is connect error/n");login_yes=0;}else if(strncmp(recvline,"220",3)==0){printf("connect success,pelase enter username/n");login_yes=1;}else {printf("220 connect is error!");login_yes=0; }//ftp用戶登錄主體部分int sendbytes,recvbytes;zeromery(name,1024);zeromery(password,1024);zeromery(recvline,1024);zeromery(sendline,1024);printf("ftp-> ");scanf("%s",name);//可以支持匿名登錄vsftpdstrcat(sendline,"USER ");strcat(sendline,name);strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("send is wrong/n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"331",3)==0){printf("331 please specify the password./n");}else{printf("recv date is error./n");login_yes=0;}zeromery(sendline,1024);zeromery(recvline,1024);printf("ftp-> ");scanf("%s",password);strcat(sendline,"PASS ");strcat(sendline,password);strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("pass send is error/n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"230",3)==0){printf("login success!/n");login_yes=1;}else {printf("pass recv is error/n");login_yes=0;}//支持斷點續傳zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"REST ");strcat(sendline,"0");strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("rest send is error!/n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes==-1){printf("rest recv date is error./n"); login_yes=0; }if(strncmp(recvline,"350 Restart position accepted (0).",34)==0){npsupport=1;printf("support 斷點續傳/n");login_yes=1;}else{npsupport=0;printf("not support 斷點續傳/n");login_yes=0; }//獲取服務器版本信息zeromery(recvline,1024);zeromery(sendline,1024);strcat(sendline,"SYST");strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes==-1){printf("syst send is error/n");login_yes=0;}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes==-1){printf("syst recv is error/n");login_yes=0;}if(strncmp(recvline,"215 UNIX Type: L8",17)==0){printf("%s",recvline);login_yes=1;}else {printf("syst recv connectin is error/n");login_yes=0;} }return login_yes; }//數組初始化 void zeromery(char *a,int len) {int i;len=sizeof(a);for(i=0;i<len;i++){a[i]=0;} }//quit函數,control_sockfd,通過實參傳遞 void ftp_quit(int control_sockfd ) {char sendline[1024];char recvline[1024];int recvbytes;int sendbytes;zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"QUIT");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("quit send is error!/n");exit(1);}recvbytes=recv(control_sockfd,recvline,strlen(recvline),0);if(strncmp(recvline,"221",3)==0){printf("221 bye!^_^");exit(1);}else{printf("quit recv is error!/n");exit(1);} }//mkd,在所在路徑中創建目錄 函數 void ftp_creat_mkd(char *path,int control_sockfd) {char sendline[1024];char recvline[1024];zeromery(sendline,1024);zeromery(recvline,1024);int recvbytes,sendbytes;int issuccess;strcat(sendline,"MKD ");strcat(sendline,path);strcat(sendline,"/r/n");printf("%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("mkd send is error!");exit(1);}recvbytes=recv(control_sockfd,recvline,strlen(recvline),0);if(strncmp(recvline,"257",3)==0){issuccess=1;}else{issuccess=0;} }//改變目錄函數chdir void ftp_changdir(char *dir,int control_sockfd) {char sendline[1024];char recvline[1024];int recvbytes,sendbytes;zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"CWD ");strcat(sendline,dir);strcat(sendline,"/r/n");printf("%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("cwd send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("cwd recv is error!/n");}if(strncmp(recvline,"250",3)==0){char buf[55];snprintf(buf,39,">>> %s/n",recvline);printf("%s/n",buf);}else{printf("cwd chdir is error!/n");exit(1);} }//pwd 命令函數 //在應答中返回當前工作目錄,“pwd”+/r/n void ftp_pwd(int control_sockfd) {int recvbytes,sendbytes;char sendline[1024],recvline[1024];zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"PWD");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("pwd,send is error/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"257",3)==0){int i=0;char *ptr;char currendir[1024];zeromery(currendir,1024);ptr=recvline+5;while(*(ptr)!='"'){currendir[i++]=*(ptr);ptr++;}currendir[i]='/0';printf("current directory is:%s/n",currendir);}else{printf("pwd,recv is error!/n");} }//獲取服務器文件列表 //list命令,是數據通道,通過的是21端口。the function 's struct is "the data //transport mode"(ascii or b) puls "the data mode to transport"(pasv or port//) puls "the list command" void ftp_list(int control_sockfd) {int pasv_or_port;// 定義the ftp協議的兩種不同工作modeint recvbytes,sendbytes;char sendline[1024],recvline[1024];struct sockaddr_in serv_addr;int i,j;int flag=0;int data_sockfd;//用戶來選擇pasv 或者是 port mode(默認的是pasv模式)char selectdata_mode_tran[1024];zeromery(selectdata_mode_tran,1024);zeromery(sendline,1024);zeromery(recvline,1024);//printf("ftp->ftp協議工作方式選擇(pasv or port)/n");//printf("ftp->"); // scanf("%s",selectdata_mode_tran);//if(strncmp(selectdata_mode_tran,"pasv",4)==0)//{pasv_or_port=0; // } // if(strncmp(selectdata_mode_tran,"port",4)==0) // { // pasv_or_port=1; // }//pasv modeif(pasv_or_port==0){strcat(sendline,"PASV");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("pasv send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("pasv recv is error!/n");}if(strncmp(recvline,"227",3)==0){printf("%s/n",recvline);}else{printf("pasv recv is error!/n");} //處理ftp server 端口char *ptr1,*ptr2;char num[1024];zeromery(num,1024);//取低位字節ptr1=recvline+strlen(recvline);while(*(ptr1)!=')'){ptr1--;}ptr2=ptr1;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);i=atoi(num);//將字符串轉換成整數//取高位字節zeromery(num,1024);ptr1=ptr2;ptr2--;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);j=atoi(num);//初始化服務器數據連接時的端口信息int data_serviceport;data_serviceport=j*256+i;data_sockfd=socket(AF_INET,SOCK_STREAM,0);serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(data_serviceport);if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1){printf("pasv data connect is error!/n");}}//port modeif(pasv_or_port==1){data_sockfd=socket(AF_INET,SOCK_STREAM,0);if(data_sockfd<0){printf("創建數據端口連接失敗!/n");}serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(SERV_PORT);int ret;int addrlen;ret=sizeof(struct sockaddr_in);getsockname(data_sockfd,(SA*)&serv_addr,&ret);//處理port 后面要帶的參數char ip[1024];int i,j;char data[1024];zeromery(ip,1024);zeromery(data,1024);inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));printf("%s/n",ip);i=data_sockfd/256;j=data_sockfd%256;//將點分十進制的點轉換為逗號。char *ptr1;ptr1=ip;while(*(ptr1)!='/0'){if(*(ptr1)=='.'){*(ptr1)=',';}ptr1++;}strcat(sendline,"PORT ");strcat(sendline,ip);strcat(sendline,",");strcat(sendline,itoa(i,data,10));strcat(sendline,",");strcat(sendline,itoa(j,data,10));strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("port send is error!/n");exit(1);}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("%s/n",recvline);}else{printf("port recv is error!/n");}}//typezeromery(recvline,1024);zeromery(sendline,1024);strcat(sendline,"TYPE ");strcat(sendline,"I");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf(" type send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("使用二進制傳輸數據/n");}else{printf("type recv is error!/n");}//listzeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"LIST");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("list send is error!/n");} recvdata:sleep(1);recvbytes=recv(data_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){close(data_sockfd);goto ending;}printf("%s",recvline);if(flag==0){zeromery(recvline,1024);recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"226",3)!=0){flag=1;goto recvdata;}} ending:if(flag!=1){zeromery(recvline,1024);}close(data_sockfd); }//itoa 函數的實現(整數轉換成字符串) char *itoa(int value, char *string, int radix) {char tmp[33];char *tp = tmp;int i;unsigned v;int sign;char *sp;sign = (radix == 10 && value < 0);if (sign)v = -value;elsev = (unsigned)value;while (v || tp == tmp){i = v % radix;v = v / radix;if (i < 10)*tp++ = i+'0';else*tp++ = i + 'a' - 10;}if (string == 0)string = (char *)malloc((tp-tmp)+sign+1);sp = string;if (sign)*sp++ = '-';while (tp > tmp)*sp++ = *--tp;*sp = 0;return string; }//back 返回上一級函數,相當于cd /; void ftp_back(int control_sockfd) {char sendline[1024],recvline[1024];int recvbytes,sendbytes;zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"CDUP");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("cdup send is error !/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("cdup recv is error !/n");}if(strncmp(recvline,"250",3)==0){printf("請求的文件操作已經成功/n");} } //stru命令的實現 void ftp_stru(int control_sockfd) {int recvbytes,sendbytes;char sendline[1024],recvline[1024];zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"STRU");strcat(sendline,"F");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("stru send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("stru recv is error!/n");}if(strncmp(recvline,"200",3)==0){f=0;}}//斷點函數的支持 void ftp_rest(int control_sockfd) {int recvbytes,sendbytes;char sendline[1024],recvline[1024];zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"REST ");strcat(sendline,"500");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("stru send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("stru recv is error!/n");}if(strncmp(recvline,"350",3)==0){printf("%s/n",recvline);} }//下載的實現函數 int ftp_download(int control_sockfd) {int pasv_or_port;// 定義the ftp協議的兩種不同工作modeint recvbytes,sendbytes;char sendline[1024],recvline[1024];struct sockaddr_in serv_addr;FILE *fd;int i,j;int data_sockfd;//restftp_rest(control_sockfd);//typezeromery(recvline,1024);zeromery(sendline,1024);strcat(sendline,"TYPE ");strcat(sendline,"I");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf(" type send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("使用二進制傳輸數據/n");}else{printf("type recv is error!/n");}if(npsupport==1){//open the fileint size;char localpathname[60];//預打開的文件路徑字符串int flags;char pathname[60];unsigned int mode;//用戶來選擇pasv 或者是 port modechar selectdata_mode_tran[1024];zeromery(selectdata_mode_tran,1024);zeromery(sendline,1024);zeromery(recvline,1024);pasv_or_port=0;//(默認是pasv模式)//pasv modeif(pasv_or_port==0){strcat(sendline,"PASV");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("pasv send is error!/n");}zeromery(recvline,1024);recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("pasv recv is error!/n");}if(strncmp(recvline,"227",3)==0){char buf[55];snprintf(buf,51,">>> %s/n",recvline);printf("%s/n",buf);}else{printf("pasv recv is error!/n");} //處理ftp server 端口char *ptr1,*ptr2;char num[1024];zeromery(num,1024);//取低位字節ptr1=recvline+strlen(recvline);while(*(ptr1)!=')'){ptr1--;}ptr2=ptr1;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);i=atoi(num);//將字符串轉換成整數//取高位字節zeromery(num,1024);ptr1=ptr2;ptr2--;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);j=atoi(num);//初始化服務器數據連接時的端口信息int data_serviceport;data_serviceport=j*256+i;data_sockfd=socket(AF_INET,SOCK_STREAM,0);serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(data_serviceport);if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1){printf("pasv data connect is error!/n");}printf("remote-file-pathname=");scanf("%s",pathname);printf("local-file-pathname=");scanf("%s",localpathname);printf("local:%s remore:%s/n",localpathname,pathname);fd=fopen(localpathname,"w+");if(fd==NULL){printf("cannot open file/n");exit(1);}//send the command retr;zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"RETR ");strcat(sendline,pathname);strcat(sendline,"/r/n");printf("%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("retr send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("retr recv is error!/n");}if(strncmp(recvline,"400",3)>0){printf("return is error!/n");}}//port mode/*if(pasv_or_port==1){data_sockfd=socket(AF_INET,SOCK_STREAM,0);if(data_sockfd<0){printf("創建數據端口連接失敗!/n");}serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(SERV_PORT);int ret;int addrlen;ret=sizeof(struct sockaddr_in);getsockname(data_sockfd,(SA*)&serv_addr,&ret);//處理port 后面要帶的參數char ip[1024];int i,j;char data[1024];zeromery(ip,1024);zeromery(data,1024);inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));printf("%s/n",ip);i=data_sockfd/256;j=data_sockfd%256;//將點分十進制的點轉換為逗號。char *ptr1;ptr1=ip;while(*(ptr1)!='/0'){if(*(ptr1)=='.'){*(ptr1)=',';}ptr1++;}strcat(sendline,"PORT ");strcat(sendline,ip);strcat(sendline,",");strcat(sendline,itoa(i,data,10));strcat(sendline,",");strcat(sendline,itoa(j,data,10));strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("port send is error!/n");exit(1);}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("%s/n",recv);}else{printf("port recv is error!/n");}}*///begin to transpotr datasleep(1);int flag=0;char buffer[65536]; recvdata:zeromery(buffer,1024);recvbytes=recv(data_sockfd,buffer,sizeof(buffer),0);if(recvbytes<0){close(data_sockfd);goto end;}fwrite(buffer,1,recvbytes,fd);zeromery(recvline,1024);recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(flag==0){if(strncmp(recvline,"226",3)!=0){flag=1;goto recvdata;}} end:if(flag!=1){zeromery(recvline,1024);shutdown(data_sockfd,SHUT_WR);close(data_sockfd);}close(data_sockfd); /* int err;char buffer[65535];err=read(data_sockfd,buffer,sizeof(buffer));sleep(5);fwrite(buffer,1,err,fd);sleep(5);zeromery(recvline,1024);recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"226",3)==0){end: printf("226 transfer complete/n");close(data_sockfd);}*/return 0;} }//up 函數 int ftp_up(int control_sockfd) {int pasv_or_port;// 定義the ftp協議的兩種不同工作modeint recvbytes,sendbytes;char sendline[1024],recvline[1024];struct sockaddr_in serv_addr;FILE *fd;int i,j;int data_sockfd;//typezeromery(recvline,1024);zeromery(sendline,1024);strcat(sendline,"TYPE ");strcat(sendline,"I");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf(" type send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("使用二進制傳輸數據/n");}else{printf("type recv is error!/n");}if(npsupport==1){//open the fileint size;char localpathname[60];//預打開的文件路徑字符串int flags;char pathname[60];unsigned int mode;//用戶來選擇pasv 或者是 port modechar selectdata_mode_tran[1024];zeromery(selectdata_mode_tran,1024);zeromery(sendline,1024);zeromery(recvline,1024);pasv_or_port=0;//(默認是pasv模式)//pasv modeif(pasv_or_port==0){strcat(sendline,"PASV");strcat(sendline,"/r/n");sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("pasv send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("pasv recv is error!/n");}if(strncmp(recvline,"227",3)==0){char buf[55];snprintf(buf,51,">>> %s/n",recvline);printf("%s/n",buf);}else{printf("pasv recv is error!/n");} //處理ftp server 端口char *ptr1,*ptr2;char num[1024];zeromery(num,1024);//取低位字節ptr1=recvline+strlen(recvline);while(*(ptr1)!=')'){ptr1--;}ptr2=ptr1;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);i=atoi(num);//將字符串轉換成整數//取高位字節zeromery(num,1024);ptr1=ptr2;ptr2--;while(*(ptr2)!=',')ptr2--;strncpy(num,ptr2+1,ptr1-ptr2-1);j=atoi(num);//初始化服務器數據連接時的端口信息int data_serviceport;data_serviceport=j*256+i;data_sockfd=socket(AF_INET,SOCK_STREAM,0);serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(data_serviceport);if(connect(data_sockfd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr))==-1){printf("pasv data connect is error!/n");}printf("local-file-pathname=");scanf("%s",pathname);printf("remote-file-pathname=");scanf("%s",localpathname);printf("local:%s remore:%s/n",localpathname,pathname);fd=fopen(pathname,"r");if(fd==NULL){printf("cannot open file,請重新輸入!/n");}//send the command retr;zeromery(sendline,1024);zeromery(recvline,1024);strcat(sendline,"STOR ");strcat(sendline,localpathname);strcat(sendline,"/r/n");printf("%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("stor send is error!/n");}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(recvbytes<0){printf("retr recv is error!/n");}if(strncmp(recvline,"150",3)==0){char buf[55];snprintf(buf,25,">>> %s/n",recvline);printf("%s/n",buf);}}//port mode/*if(pasv_or_port==1){data_sockfd=socket(AF_INET,SOCK_STREAM,0);if(data_sockfd<0){printf("創建數據端口連接失敗!/n");}serv_addr.sin_family=AF_INET;serv_addr.sin_addr.s_addr=INADDR_ANY;serv_addr.sin_port=htons(SERV_PORT);int ret;int addrlen;ret=sizeof(struct sockaddr_in);getsockname(data_sockfd,(SA*)&serv_addr,&ret);//處理port 后面要帶的參數char ip[1024];int i,j;char data[1024];zeromery(ip,1024);zeromery(data,1024);inet_ntop(AF_INET,&(serv_addr.sin_addr),ip,sizeof(ip));printf("%s/n",ip);i=data_sockfd/256;j=data_sockfd%256;//將點分十進制的點轉換為逗號。char *ptr1;ptr1=ip;while(*(ptr1)!='/0'){if(*(ptr1)=='.'){*(ptr1)=',';}ptr1++;}strcat(sendline,"PORT ");strcat(sendline,ip);strcat(sendline,",");strcat(sendline,itoa(i,data,10));strcat(sendline,",");strcat(sendline,itoa(j,data,10));strcat(sendline,"/r/n");printf("--->%s/n",sendline);sendbytes=send(control_sockfd,sendline,strlen(sendline),0);if(sendbytes<0){printf("port send is error!/n");exit(1);}recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"200",3)==0){printf("%s/n",recv);}else{printf("port recv is error!/n");}}*///begin to transpotr datawhile(!feof(fd)){ char buffer[65536];zeromery(buffer,sizeof(buffer));int size;size=fread(buffer,1,sizeof(buffer),fd);if(ferror(fd)){printf("read file data is error!/n");break;}else{zeromery(sendline,1024);sendbytes=send(data_sockfd,buffer,size,0);printf("傳輸了 %d 個字節/n",sendbytes);}close(data_sockfd);recvbytes=recv(control_sockfd,recvline,sizeof(recvline),0);if(strncmp(recvline,"226",3)==0){printf("226 transfer complete");break;}}return 0;} }?
總結
以上是生活随笔為你收集整理的FTP 客户端C实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小程序循环里做字符串拼接_昨天还在for
- 下一篇: java流类图结构_java学习之IO流