1. 程式人生 > >linux下用C編寫ftp客戶端

linux下用C編寫ftp客戶端

這是一個大作業,要求能夠模擬ftp協議,實現一個ftp客戶端,然後要求能夠實現相應的功能,主要是能夠實現ls,pwd,cwd(cd),put和get功能。然後是在被動模式下來實現這些功能。那麼首先我們需要對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 命令

USER: 指定使用者名稱。通常是控制連線後第一個發出的命令。“USER gaoleyi\r\n”: 使用者名稱為gaoleyi 登入。

PASS: 指定使用者密碼。該命令緊跟 USER 命令後。“PASS gaoleyi\r\n”:密碼為 gaoleyi。

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。

QUIT: 關閉與伺服器的連線。

FTP 響應碼

客戶端傳送 FTP 命令後,伺服器返回響應碼。

響應碼用三位數字編碼表示:

第一個數字給出了命令狀態的一般性指示,比如響應成功、失敗或不完整。

第二個數字是響應型別的分類,如 2 代表跟連線有關的響應,3 代表使用者認證。

第三個數字提供了更加詳細的資訊。

第一個數字的含義如下:

1 表示伺服器正確接收資訊,還未處理。

2 表示伺服器已經正確處理資訊。

3 表示伺服器正確接收資訊,正在處理。

4 表示資訊暫時錯誤。

5 表示資訊永久錯誤。

第二個數字的含義如下:

0 表示語法。

1 表示系統狀態和資訊。

2 表示連線狀態。

3 表示與使用者認證有關的資訊。

4 表示未定義。

5 表示與檔案系統有關的資訊。

Socket 程式設計的幾個重要步驟

Socket 客戶端程式設計主要步驟如下:

  1. socket() 建立一個 Socket
  2. connect() 與伺服器連線
  3. write() 和 read() 進行會話
  4. close() 關閉 Socket

FTP客戶端實現登入功能:

首先是開啟一個tcp連線

int cliopen(char *host,int port)
{
    int control_sock;
    struct hostent *ht = NULL;
    control_sock = socket(AF_INET,SOCK_STREAM,0);
    if(control_sock < 0)
    {
       printf("socket error\n");
       return -1;
    }
    ht = gethostbyname(host);
    if(!ht)
    { 
        return -1;
    }
    //memcpy(&servaddr.sin_addr.s_addr,ht->h_addr,ht->h_length);
   
    memset(&servaddr,0,sizeof(struct sockaddr_in));
    memcpy(&servaddr.sin_addr.s_addr,ht->h_addr,ht->h_length);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    
    if(connect(control_sock,(struct sockaddr*)&servaddr,sizeof(struct sockaddr)) == -1)
    {
        return -1;
    }
    return control_sock;
}

執行這個函式後,會返回一個新建立的TCP連線的序號,然後就是執行主函數了:cmd_tcp(); 注意這個函式中用到了一個select函式,這個函式是用來選擇和監聽從鍵盤輸入還是伺服器端返回,這個函式是socket程式設計中很重要的一個函式。

void cmd_tcp(int sockfd)
{
    int maxfdp1,nread,nwrite,fd,replycode,tag=0,data_sock;
    int port;
    char *pathname;
    fd_set rset;
    FD_ZERO(&rset);
    maxfdp1 = sockfd + 1;
    for(;;)
    {
         FD_SET(STDIN_FILENO,&rset);
         FD_SET(sockfd,&rset);
         if(select(maxfdp1,&rset,NULL,NULL,NULL)<0)
         {
             printf("select error\n");
         }
         if(FD_ISSET(STDIN_FILENO,&rset))                       //判斷是從鍵盤輸入的,還是從伺服器端返回的
         {
              //nwrite = 0;
              //nread = 0;
              bzero(wbuf,MAXBUF);          //zero               //當然要注意的是,每次在重新讀取緩衝區裡的內容時,需要將原來的緩衝區清空,用到的是bzero函式,或者也可以用memset函式
              bzero(rbuf1,MAXBUF);
              if((nread = read(STDIN_FILENO,rbuf1,MAXBUF)) <0)
                   printf("read error from stdin\n");
              nwrite = nread + 5;
              //printf("%d\n",nread);      
              if(replycode == USERNAME)
              {
                  sprintf(wbuf,"USER %s",rbuf1);
              
                 if(write(sockfd,wbuf,nwrite) != nwrite)
                 {
                     printf("write error\n");
                 }
                 //printf("%s\n",wbuf);
                 //memset(rbuf1,0,sizeof(rbuf1));
                 //memset(wbuf,0,sizeof(wbuf));
                 //printf("1:%s\n",wbuf);
              }


              if(replycode == PASSWORD)
              {
                   //printf("%s\n",rbuf1);
                   sprintf(wbuf,"PASS %s",rbuf1);
                   if(write(sockfd,wbuf,nwrite) != nwrite)
                      printf("write error\n");
                   //bzero(rbuf,sizeof(rbuf));
                   //printf("%s\n",wbuf);
                   //printf("2:%s\n",wbuf);
              }
              if(replycode == 550 || replycode == LOGIN || replycode == CLOSEDATA || replycode == PATHNAME || replycode == ACTIONOK)
              {
          if(strncmp(rbuf1,"pwd",3) == 0)
              {   
                     //printf("%s\n",rbuf1);
                     sprintf(wbuf,"%s","PWD\n");
                     write(sockfd,wbuf,4);
                     continue; 
                 }
                 if(strncmp(rbuf1,"quit",4) == 0)
                 {
                     sprintf(wbuf,"%s","QUIT\n");
                     write(sockfd,wbuf,5);
                     //close(sockfd);
                    if(close(sockfd) <0)
                       printf("close error\n");
                    break;
                 }
                 if(strncmp(rbuf1,"cwd",3) == 0)
                 {
                     //sprintf(wbuf,"%s","PASV\n");
                     sprintf(wbuf,"%s",rbuf1);
                     write(sockfd,wbuf,nread);
                     
                     //sprintf(wbuf1,"%s","CWD\n");
                     
                     continue;
                 }
                 if(strncmp(rbuf1,"ls",2) == 0)
                 {
                     tag = 2;
                     //printf("%s\n",rbuf1);
                     sprintf(wbuf,"%s","PASV\n");
                     //printf("%s\n",wbuf);
                     write(sockfd,wbuf,5);
                     //read
                     //sprintf(wbuf1,"%s","LIST -al\n");
                     nwrite = 0;
                     //write(sockfd,wbuf1,nwrite);
                     //ftp_list(sockfd);
                     continue;    
                 }
                 if(strncmp(rbuf1,"get",3) == 0)
                 {
                     tag = 1;
                     sprintf(wbuf,"%s","PASV\n");                   
                     //printf("%s\n",s(rbuf1));
                     //char filename[100];
                     s(rbuf1,filename);
                     printf("%s\n",filename);
                     write(sockfd,wbuf,5);
                     continue;
                 }
                 if(strncmp(rbuf1,"put",3) == 0)
                 {
                     tag = 3;
                     sprintf(wbuf,"%s","PASV\n");
                     st(rbuf1,filename);
                     printf("%s\n",filename);
                     write(sockfd,wbuf,5);
                     continue;
                 }
              } 
                    /*if(close(sockfd) <0)
                       printf("close error\n");*/
         }
         if(FD_ISSET(sockfd,&rset))
         {
             bzero(rbuf,strlen(rbuf));
             if((nread = recv(sockfd,rbuf,MAXBUF,0)) <0)
                  printf("recv error\n");
             else if(nread == 0)
               break;
           
             if(strncmp(rbuf,"220",3) ==0 || strncmp(rbuf,"530",3)==0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                 strcat(rbuf,"your name:");
                 //printf("%s\n",rbuf);
                 nread += 12;
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                 replycode = USERNAME;
             }
             if(strncmp(rbuf,"331",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n")*/;
                strcat(rbuf,"your password:");
                nread += 16;
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = PASSWORD;
             }
             if(strncmp(rbuf,"230",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = LOGIN;
             }
             if(strncmp(rbuf,"257",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = PATHNAME;  
             }
             if(strncmp(rbuf,"226",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = CLOSEDATA;
             }
             if(strncmp(rbuf,"250",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = ACTIONOK;
             }
             if(strncmp(rbuf,"550",3) == 0)
             {
                replycode = 550;
             }
             /*if(strncmp(rbuf,"150",3) == 0)
             {
                if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");
             }*/    
             //fprintf(stderr,"%d\n",1);
             if(strncmp(rbuf,"227",3) == 0)
             {
                //printf("%d\n",1);
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                   printf("write error to stdout\n");*/
                int port1 = strtosrv(rbuf);
                printf("%d\n",port1);
                printf("%s\n",host);
                data_sock = cliopen(host,port1);
       


//bzero(rbuf,sizeof(rbuf));
                //printf("%d\n",fd);
                //if(strncmp(rbuf1,"ls",2) == 0)
                if(tag == 2)
                {
                   write(sockfd,"list\n",strlen("list\n"));
                   ftp_list(data_sock);
                   /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                       printf("write error to stdout\n");*/
                   
                }
                //else if(strncmp(rbuf1,"get",3) == 0)
                else if(tag == 1)
                {
                    //sprintf(wbuf,"%s","RETR\n");
                    //printf("%s\n",wbuf);
                    //int str = strlen(filename);
                    //printf("%d\n",str);
                    sprintf(wbuf,"RETR %s\n",filename);
                    printf("%s\n",wbuf);
                    //int p = 5 + str + 1;


                    printf("%d\n",write(sockfd,wbuf,strlen(wbuf)));
                    //printf("%d\n",p);
                    ftp_get(data_sock,filename);
                }
                else if(tag == 3)
                {
                    sprintf(wbuf,"STOR %s\n",filename);
                    printf("%s\n",wbuf);
                    write(sockfd,wbuf,strlen(wbuf));
                    ftp_put(data_sock,filename);
                }
                nwrite = 0;     
             }
             /*if(strncmp(rbuf,"150",3) == 0)
             {
                 if(write(STDOUT_FILENO,rbuf,nread) != nread)
                     printf("write error to stdout\n");
             }*/
             //printf("%s\n",rbuf);
             if(write(STDOUT_FILENO,rbuf,nread) != nread)
                 printf("write error to stdout\n");
             /*else 
                 printf("%d\n",-1);*/            
         }
    }
}

過程,首先是從鍵盤輸入,然後伺服器端會向我的ftp客戶端傳送執行請求的返回碼,然後我的程式碼就通過select函式來捕獲這些個驗證碼replycode,然後程式碼根據這個replycode,來執行相應的不同的程式碼。

接下來是具體的針對每一個功能的程式碼,首先是將我被動模式下,新生成的埠號計算新生成的埠號的程式碼,注意,這裡用到了sscanf函式

int strtosrv(char *str)
{
   int addr[6];
   //printf("%s\n",str);
   sscanf(str,"%*[^(](%d,%d,%d,%d,%d,%d)",&addr[0],&addr[1],&addr[2],&addr[3],&addr[4],&addr[5]);   //sscanf函式是從一個字串中讀進與制定格式相符的資料,可以和sprintf來對應。這裡用到了一點正則表示式,用來匹配相應的字元結果。
   bzero(host,strlen(host)); 
   sprintf(host,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]);
   int port = addr[4]*256 + addr[5];
   return port;
}

而在我上傳和下載的過程中,我也得先取得put filename和get filename後面的filename,這個就需要呼叫一個函式來將這個filename分離並取出來。

int s(char *str,char *s2)
{
    //char s1[100];
     
    return sscanf(str," get %s",s2) == 1;
   
}

接下來是顯示的函式ftp_list

void ftp_list(int sockfd)                                  //注意,list顯示內容不用先進入被動模式,而是直接就可以顯示資訊了
{
    int nread;
    for(;;)
    {
        if((nread = recv(sockfd,rbuf1,MAXBUF,0)) < 0)
        {
            printf("recv error\n");
        }
        else if(nread == 0)
        {
            //printf("over\n");
            break;
        }
        if(write(STDOUT_FILENO,rbuf1,nread) != nread)              //這個STDOUT_FILENO表示輸出到螢幕
            printf("send error to stdout\n");
        /*else
            printf("read something\n");*/
    }
    if(close(sockfd) < 0)
        printf("close error\n");
}

接下來是ftp_get函式,是用從ftp伺服器端下載檔案用的

int ftp_get(int sck,char *pDownloadFileName)
{
   int handle = open(pDownloadFileName,O_WRONLY | O_CREAT | O_TRUNC, S_IREAD| S_IWRITE);
   int nread;
   printf("%d\n",handle);
   /*if(handle == -1) 
       return -1;*/
   for(;;)
   {
       if((nread = recv(sck,rbuf1,MAXBUF,0)) < 0)
       {
          printf("receive error\n");
       }
       else if(nread == 0)
       {
          printf("over\n");
          break;
       }
    //   printf("%s\n",rbuf1);
       if(write(handle,rbuf1,nread) != nread)
           printf("receive error from server!");
       if(write(STDOUT_FILENO,rbuf1,nread) != nread)
           printf("receive error from server!");
   }
       if(close(sck) < 0)
           printf("close error\n");
}

這個是先建立連線的過程圖,然後就是針對get請求,具體的詳細的過程圖。


這個是進入被動模式的過程,然後就是在進入被動模式後,開始下載檔案,

接下來是上傳檔案,上傳檔案的過程與下載檔案的過程如出一撤,基本過程是類似的,但是有些地方需要當心,應該先open本地的檔案,然後是用read函式將本地的檔案如出來,然後呼叫write函式向新建立的埠傳送這個檔案。


以下是程式碼部分:

int ftp_put(int sck,char *pUploadFileName_s)
{
   //int c_sock;
   int handle = open(pUploadFileName_s,O_RDWR);
   int nread;
   if(handle == -1)
       return -1;
   //ftp_type(c_sock,"I");
   for(;;)
   {
       if((nread = read(handle,rbuf1,MAXBUF)) < 0)
       {
            printf("read error!");
       }
       else if(nread == 0)
          break;
       if(write(STDOUT_FILENO,rbuf1,nread) != nread)
            printf("send error!");
       if(write(sck,rbuf1,nread) != nread)
            printf("send error!");
   }
   if(close(sck) < 0)
        printf("close error\n");
}

完整程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>


#define MAXBUF 1024
#define STDIN_FILENO 1
#define STDOUT_FILENO 0


#define USERNAME 220
#define PASSWORD 331
#define LOGIN 230
#define PATHNAME 257
#define CLOSEDATA 226
#define ACTIONOK 250


char *rbuf,*rbuf1,*wbuf,*wbuf1;


char filename[100];
char *host;


struct sockaddr_in servaddr;


int cliopen(char *host,int port);
int strtosrv(char *str);
int ftp_get(int sck,char *pDownloadFileName);
int ftp_put(int sck,char *pUploadFileName_s);
void cmd_tcp(int sockfd);


int main(int argc,char *argv[])
{
    int fd;
    if(0 != argc -2)
    {
        printf("%s\n","missing <hostname>");
        exit(0);


    }
    host = argv[1];
    int port = 21;
   
    rbuf = (char *)malloc(MAXBUF*sizeof(char));
    rbuf1 = (char *)malloc(MAXBUF*sizeof(char));
    wbuf = (char *)malloc(MAXBUF*sizeof(char));
    wbuf1 = (char *)malloc(MAXBUF*sizeof(char));
    
    fd = cliopen(host,port);
    cmd_tcp(fd);
    exit(0);
}






int cliopen(char *host,int port)
{
    int control_sock;
    struct hostent *ht = NULL;
    control_sock = socket(AF_INET,SOCK_STREAM,0);
    if(control_sock < 0)
    {
       printf("socket error\n");
       return -1;
    }
    ht = gethostbyname(host);
    if(!ht)
    { 
        return -1;
    }
    //memcpy(&servaddr.sin_addr.s_addr,ht->h_addr,ht->h_length);
   
    memset(&servaddr,0,sizeof(struct sockaddr_in));
    memcpy(&servaddr.sin_addr.s_addr,ht->h_addr,ht->h_length);
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);
    
    if(connect(control_sock,(struct sockaddr*)&servaddr,sizeof(struct sockaddr)) == -1)
    {
        return -1;
    }
    return control_sock;
}


int s(char *str,char *s2)
{
    //char s1[100];
     
    return sscanf(str," get %s",s2) == 1;
   
}


int st(char *str,char *s1)
{
    return sscanf(str," put %s",s1) == 1;
}


int strtosrv(char *str)
{
   int addr[6];
   //printf("%s\n",str);
   sscanf(str,"%*[^(](%d,%d,%d,%d,%d,%d)",&addr[0],&addr[1],&addr[2],&addr[3],&addr[4],&addr[5]);
   bzero(host,strlen(host));
   sprintf(host,"%d.%d.%d.%d",addr[0],addr[1],addr[2],addr[3]);
   int port = addr[4]*256 + addr[5];
   return port;
}


void ftp_list(int sockfd)
{
    int nread;
    for(;;)
    {
        if((nread = recv(sockfd,rbuf1,MAXBUF,0)) < 0)
        {
            printf("recv error\n");
        }
        else if(nread == 0)
        {
            //printf("over\n");
            break;
        }
        if(write(STDOUT_FILENO,rbuf1,nread) != nread)
            printf("send error to stdout\n");
        /*else
            printf("read something\n");*/
    }
    if(close(sockfd) < 0)
        printf("close error\n");
}


int ftp_get(int sck,char *pDownloadFileName)
{
   int handle = open(pDownloadFileName,O_WRONLY | O_CREAT | O_TRUNC, S_IREAD| S_IWRITE);
   int nread;
   printf("%d\n",handle);
   /*if(handle == -1) 
       return -1;*/
   for(;;)
   {
       if((nread = recv(sck,rbuf1,MAXBUF,0)) < 0)
       {
          printf("receive error\n");
       }
       else if(nread == 0)
       {
          printf("over\n");
          break;
       }
    //   printf("%s\n",rbuf1);
       if(write(handle,rbuf1,nread) != nread)
           printf("receive error from server!");
       if(write(STDOUT_FILENO,rbuf1,nread) != nread)
           printf("receive error from server!");
   }
       if(close(sck) < 0)
           printf("close error\n");
}


int ftp_put(int sck,char *pUploadFileName_s)
{
   //int c_sock;
   int handle = open(pUploadFileName_s,O_RDWR);
   int nread;
   if(handle == -1)
       return -1;
   //ftp_type(c_sock,"I");
   for(;;)
   {
       if((nread = read(handle,rbuf1,MAXBUF)) < 0)
       {
            printf("read error!");
       }
       else if(nread == 0)
          break;
       if(write(STDOUT_FILENO,rbuf1,nread) != nread)
            printf("send error!");
       if(write(sck,rbuf1,nread) != nread)
            printf("send error!");
   }
   if(close(sck) < 0)
        printf("close error\n");
}




void cmd_tcp(int sockfd)
{
    int maxfdp1,nread,nwrite,fd,replycode,tag=0,data_sock;
    int port;
    char *pathname;
    fd_set rset;
    FD_ZERO(&rset);
    maxfdp1 = sockfd + 1;
    for(;;)
    {
         FD_SET(STDIN_FILENO,&rset);
         FD_SET(sockfd,&rset);
         if(select(maxfdp1,&rset,NULL,NULL,NULL)<0)
         {
             printf("select error\n");
         }
         if(FD_ISSET(STDIN_FILENO,&rset))
         {
              //nwrite = 0;
              //nread = 0;
              bzero(wbuf,MAXBUF);          //zero
              bzero(rbuf1,MAXBUF);
              if((nread = read(STDIN_FILENO,rbuf1,MAXBUF)) <0)
                   printf("read error from stdin\n");
              nwrite = nread + 5;
              //printf("%d\n",nread);      
              if(replycode == USERNAME)
              {
                  sprintf(wbuf,"USER %s",rbuf1);
              
                 if(write(sockfd,wbuf,nwrite) != nwrite)
                 {
                     printf("write error\n");
                 }
                 //printf("%s\n",wbuf);
                 //memset(rbuf1,0,sizeof(rbuf1));
                 //memset(wbuf,0,sizeof(wbuf));
                 //printf("1:%s\n",wbuf);
              }


              if(replycode == PASSWORD)
              {
                   //printf("%s\n",rbuf1);
                   sprintf(wbuf,"PASS %s",rbuf1);
                   if(write(sockfd,wbuf,nwrite) != nwrite)
                      printf("write error\n");
                   //bzero(rbuf,sizeof(rbuf));
                   //printf("%s\n",wbuf);
                   //printf("2:%s\n",wbuf);
              }
              if(replycode == 550 || replycode == LOGIN || replycode == CLOSEDATA || replycode == PATHNAME || replycode == ACTIONOK)
              {
          if(strncmp(rbuf1,"pwd",3) == 0)
              {   
                     //printf("%s\n",rbuf1);
                     sprintf(wbuf,"%s","PWD\n");
                     write(sockfd,wbuf,4);
                     continue; 
                 }
                 if(strncmp(rbuf1,"quit",4) == 0)
                 {
                     sprintf(wbuf,"%s","QUIT\n");
                     write(sockfd,wbuf,5);
                     //close(sockfd);
                    if(close(sockfd) <0)
                       printf("close error\n");
                    break;
                 }
                 if(strncmp(rbuf1,"cwd",3) == 0)
                 {
                     //sprintf(wbuf,"%s","PASV\n");
                     sprintf(wbuf,"%s",rbuf1);
                     write(sockfd,wbuf,nread);
                     
                     //sprintf(wbuf1,"%s","CWD\n");
                     
                     continue;
                 }
                 if(strncmp(rbuf1,"ls",2) == 0)
                 {
                     tag = 2;
                     //printf("%s\n",rbuf1);
                     sprintf(wbuf,"%s","PASV\n");
                     //printf("%s\n",wbuf);
                     write(sockfd,wbuf,5);
                     //read
                     //sprintf(wbuf1,"%s","LIST -al\n");
                     nwrite = 0;
                     //write(sockfd,wbuf1,nwrite);
                     //ftp_list(sockfd);
                     continue;    
                 }
                 if(strncmp(rbuf1,"get",3) == 0)
                 {
                     tag = 1;
                     sprintf(wbuf,"%s","PASV\n");                   
                     //printf("%s\n",s(rbuf1));
                     //char filename[100];
                     s(rbuf1,filename);
                     printf("%s\n",filename);
                     write(sockfd,wbuf,5);
                     continue;
                 }
                 if(strncmp(rbuf1,"put",3) == 0)
                 {
                     tag = 3;
                     sprintf(wbuf,"%s","PASV\n");
                     st(rbuf1,filename);
                     printf("%s\n",filename);
                     write(sockfd,wbuf,5);
                     continue;
                 }
              } 
                    /*if(close(sockfd) <0)
                       printf("close error\n");*/
         }
         if(FD_ISSET(sockfd,&rset))
         {
             bzero(rbuf,strlen(rbuf));
             if((nread = recv(sockfd,rbuf,MAXBUF,0)) <0)
                  printf("recv error\n");
             else if(nread == 0)
               break;
           
             if(strncmp(rbuf,"220",3) ==0 || strncmp(rbuf,"530",3)==0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                 strcat(rbuf,"your name:");
                 //printf("%s\n",rbuf);
                 nread += 12;
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                 replycode = USERNAME;
             }
             if(strncmp(rbuf,"331",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n")*/;
                strcat(rbuf,"your password:");
                nread += 16;
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = PASSWORD;
             }
             if(strncmp(rbuf,"230",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = LOGIN;
             }
             if(strncmp(rbuf,"257",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = PATHNAME;  
             }
             if(strncmp(rbuf,"226",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = CLOSEDATA;
             }
             if(strncmp(rbuf,"250",3) == 0)
             {
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");*/
                replycode = ACTIONOK;
             }
             if(strncmp(rbuf,"550",3) == 0)
             {
                replycode = 550;
             }
             /*if(strncmp(rbuf,"150",3) == 0)
             {
                if(write(STDOUT_FILENO,rbuf,nread) != nread)
                    printf("write error to stdout\n");
             }*/    
             //fprintf(stderr,"%d\n",1);
             if(strncmp(rbuf,"227",3) == 0)
             {
                //printf("%d\n",1);
                /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                   printf("write error to stdout\n");*/
                int port1 = strtosrv(rbuf);
                printf("%d\n",port1);
                printf("%s\n",host);
                data_sock = cliopen(host,port1);
       


//bzero(rbuf,sizeof(rbuf));
                //printf("%d\n",fd);
                //if(strncmp(rbuf1,"ls",2) == 0)
                if(tag == 2)
                {
                   write(sockfd,"list\n",strlen("list\n"));
                   ftp_list(data_sock);
                   /*if(write(STDOUT_FILENO,rbuf,nread) != nread)
                       printf("write error to stdout\n");*/
                   
                }
                //else if(strncmp(rbuf1,"get",3) == 0)
                else if(tag == 1)
                {
                    //sprintf(wbuf,"%s","RETR\n");
                    //printf("%s\n",wbuf);
                    //int str = strlen(filename);
                    //printf("%d\n",str);
                    sprintf(wbuf,"RETR %s\n",filename);
                    printf("%s\n",wbuf);
                    //int p = 5 + str + 1;


                    printf("%d\n",write(sockfd,wbuf,strlen(wbuf)));
                    //printf("%d\n",p);
                    ftp_get(data_sock,filename);
                }
                else if(tag == 3)
                {
                    sprintf(wbuf,"STOR %s\n",filename);
                    printf("%s\n",wbuf);
                    write(sockfd,wbuf,strlen(wbuf));
                    ftp_put(data_sock,filename);
                }
                nwrite = 0;     
             }
             /*if(strncmp(rbuf,"150",3) == 0)
             {
                 if(write(STDOUT_FILENO,rbuf,nread) != nread)
                     printf("write error to stdout\n");
             }*/
             //printf("%s\n",rbuf);
             if(write(STDOUT_FILENO,rbuf,nread) != nread)
                 printf("write error to stdout\n");
             /*else 
                 printf("%d\n",-1);*/            
         }
    }
}

執行的效果圖:

首先是登陸:

,如果ftp連線成功,是會返回一個220,提示使用者輸入登陸名

接下來是輸入使用者的登入名,

,然後是輸入我的賬號,接下來就是輸入密碼,

,然後如果賬戶和密碼都輸對,那麼就會提示登入成功。

接下來演示pwd,

然後是ls,顯示當前路徑下的目錄的指令

,看到了返回的結果。

接下來是下載檔案檔當前路徑下。

,待會退出ftp後,可以通過ls來檢視是否已經將這個檔案下載到本地客戶端了。