1. 程式人生 > >linux下基於tcp的FTP程式設計

linux下基於tcp的FTP程式設計

客戶端輸入:

ls :           顯示本地檔案列表

service  ls:       顯示伺服器檔案列表

upload  xxx:      實現xxx檔案讀取與上傳

download  xxx :  實現xxx檔案下載與儲存

公共函式commonfunc.c 程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "commonfunc.h"
#include <stddef.h>
#include <dirent.h>


#define BACKLOG 10//最大連線數


/******************函式作用:網路初始化*****************/
int network_init(int net_type, const char* NET_IP, short NET_NUM)
{
    int sockfd;
    if(-1 == (sockfd = socket(PF_INET, SOCK_STREAM, 0)))   //建立監聽套接字
    {
        perror("Create socket error!");
        //exit(EXIT_FAILURE);
        return -1;
    }


    struct sockaddr_in sockadd;
    memset(&sockadd, 0, sizeof(sockadd));
    sockadd.sin_family = AF_INET;
    sockadd.sin_port = htons(NET_NUM);
    sockadd.sin_addr.s_addr = inet_addr(NET_IP);


    if(NET_CLIENT == net_type)
    {
           //連線伺服器
        if(-1 == connect(sockfd, (struct sockaddr*)(&sockadd),\
        sizeof(sockadd)))
        {
            perror("Connect error!");
            //exit(EXIT_FAILURE);
            return -1;
        }
    }
    else
    {
        if(NET_SERVER == net_type)
        {
                //繫結IP地址埠號
            if(-1 == bind(sockfd, (struct sockaddr*)(&sockadd),\
            sizeof(sockadd)))
            {
                perror("Bind error!");
                return -1;
            }
            
            if(-1 == listen(sockfd, BACKLOG))   //監聽客戶端
            {
                perror("Listen error!");
                return -1;
            }
        }
        else
        {
            return -1;
        }
    }
    return sockfd;
}


/**************************命令解析函式************************/
int ftp_cmd_analyse(const char* cmd)
{
    if(NULL == cmd)
    {
        return CMD_ERROR;
    }
    else
    {
          if(0 == strncmp(cmd, "ls",2))   //列出本地檔案列表指令
                    return CMD_LS;
           else
              {
            if(0 == strncmp(cmd, "server ls", 9))   //列出伺服器檔案列表指令
                return CMD_SERVER_LS;
              else
               {
                if(0 == strcmp(cmd, "quit"))   //斷開連結指令
                    return CMD_QUIT;
                else
                  {
                      if(0 == strncmp(cmd, "download ", 9))    //從伺服器下載檔案指令
                      return CMD_DOWNLOAD;
                    else
                    {
                        if(0 == strncmp(cmd, "upload ", 7))    //上傳檔案到伺服器指令
                            return CMD_UPLOAD;
                        else
                        {
                            return CMD_ERROR;
                        }
                    }
                }
            }   
        }
    }
}


/****************從伺服器端獲取檔案列表函式********************/
int ftp_getlist(int getsockfd)
{
    char GET_BUFF[BUFF_LEN];
    int readsize;
    sprintf(GET_BUFF,  "server ls");
     if(-1 == write(getsockfd, GET_BUFF, BUFF_LEN))   //向伺服器傳送命令
    {
        perror("Send cmd error!");
        return -1;
    }
    else
    {
        while(1)   //迴圈讀取
        {
            readsize = read(getsockfd, GET_BUFF, BUFF_LEN);
            if(readsize <= 0)   //讀錯誤
            {
                perror("Get list error!");
                return -1;
            }
            else
            {
                if(0 == strncmp(GET_BUFF, GET_LIST_END, \
                sizeof(GET_LIST_END)))   //判斷伺服器是否傳送完畢
                {
                  break;   //傳送完畢,退出
                }
                else
                {
                    printf("%s\n", GET_BUFF);   //伺服器端傳送完畢,顯示檔案
                }
            }
        }
    }
    return getsockfd;
}


/**************把伺服器端檔案列表傳送到客戶端函式**************/
int ftp_putlist(int putsockfd)
{
    const char* LIST_NAME=".";
    char PUT_BUFF[BUFF_LEN];
    int strn, strm;
    DIR* dp;
    struct dirent *ep;
    struct stat st;
    char LIST_PATH[256];




    dp = opendir(LIST_NAME);   //開啟目錄


    if(NULL != dp)   //開啟目錄成功
    {
        while(ep = readdir(dp))   //迴圈讀目錄
        {
            if(ep->d_name[0] != '.')   //如果不是隱藏檔案或目錄
            {
                 sprintf(PUT_BUFF,"%s",ep->d_name);
                 write(putsockfd, PUT_BUFF, BUFF_LEN);
            }
           
        }


        if(-1 == write(putsockfd, GET_LIST_END, BUFF_LEN))   //傳送結束
        {
            perror("Write endstring error!");
            return -1;
        }
    }
    else
    {
        if(-1 == write(putsockfd, GET_LIST_END, BUFF_LEN))   //傳送結束
        {
            perror("Write endstring error!");
            return -1;
        }
        perror("Can't open the directory!");
        return -1;
    }


    closedir(dp);
    return putsockfd;
}


/*************************檔案接收函式*************************/
int ftp_getfile(int getsockfd, const char* GET_FILENAME)
{
    int getfilefd;   //存放接收檔案的檔案描述符
    int getfilesize;   //實際接收的檔案大小
    char GET_BUFF[BUFF_LEN];   //接收快取




    //開啟一個檔案描述符用與儲存來自發送端的檔案
    if(-1  == (getfilefd = open(GET_FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0666)))
    {
        perror("Can't open or creat file!");
        return -1;
    }
    else
    {
        while(getfilesize = read(getsockfd, GET_BUFF, BUFF_LEN) > 0)     //接收檔案
        {
            
            if(0 == strncmp(GET_BUFF, "ERROR:", 6))   //接收檔案出錯
            {
                printf("%s", GET_BUFF);
                return -1;
            }
            else
            {
                  //取出資料包頭中包含的資料區大小
                memcpy(&getfilesize, GET_BUFF, 4);


                /*GET_BUFF+4是因為資料包前四個位元組存放的是資料長度,之後的
                1024個位元組才存放的實際的資料*/
                if(-1 == write(getfilefd, GET_BUFF+4, getfilesize))
                {
                    perror("Download error!");   //接收出錯,返回
                    close(getfilefd);   //關閉檔案
                    return -1;
                }
                if(getfilesize < (BUFF_LEN-4))   //已經讀取到檔案末尾
                        break;   //接收結束,退出
            }
        }
        close(getfilefd);   //關閉檔案
        return getfilefd;   //接收完成,返回接收到的檔案的檔案描述符。
       
    }
}


/*****************************檔案傳送函式********************************/
int ftp_putfile(int putsockfd, const char* PUT_FILENAME)
{
    int putfilefd;   //存放接收檔案的檔案描述符
    int putfilesize;   //實際接收的檔案大小
    char PUT_BUFF[BUFF_LEN];    


    if(-1 == (putfilefd = open(PUT_FILENAME, O_RDONLY)))   //開啟檔案
    {
        perror("Open error!");
        write(putsockfd, E_NOFILE, BUFF_LEN);   //把錯誤資訊寫回。
        /*如果不寫回錯誤資訊,傳送端會卡死*/
        return -1;
    }
    else
    {
       
      while((putfilesize = read(putfilefd, PUT_BUFF+4, (BUFF_LEN-4))) \
        >0)
        {
            memcpy(PUT_BUFF, &putfilesize, 4);
            if(-1 == write(putsockfd, PUT_BUFF, BUFF_LEN))
            {
                perror("Put file error!");
                close(putfilefd);
                return -1;
            }
            memset(PUT_BUFF, 0, BUFF_LEN);   //清空緩衝區
        }
    }
    close(putfilefd);
    return putfilefd;
}

//////////////////////////////////////////////////公共函式commonfunc.h標頭檔案////////////////////////////////////////////////////

#ifndef __NETWORK_H_
#define __NETWORK_H_


#define NET_SERVER  11
#define NET_CLIENT  22


#define BUFF_LEN 1028//接收發送緩衝區大小


#define CMD_LS         11//列出客戶端所有檔案
#define CMD_SERVER_LS  22//列出伺服器所有檔案
#define CMD_DOWNLOAD   33//下載檔案
#define CMD_UPLOAD     44//上傳檔案
#define CMD_QUIT       55//退出
#define CMD_ERROR      -1//錯誤


#define E_NOFILE    "ERROR:No such file or directory!\n"
#define E_DODNLOAD  "ERROR:Download error!\n"
#define E_UPLOAD    "ERROR:Upload error!\n"
#define GET_LIST_END "SUCCESS:GET LIST SUCCESS!"


int     network_init(int net_type, const char* IP_ADDRESS, \
short INET_NUM);


void    ftp_print_help(void);
int     ftp_cmd_analyse(const char* cmd);
int     ftp_getlist(int getsockfd);
int     ftp_putlist(int putsockfd);
int     ftp_getfile(int getsockfd, const char* GET_FILENAME);
int     ftp_putfile(int putsockfd, const char* PUT_FILENAME);

#endif

//////////////////////////////////////////////////////////////////////客戶端程式///////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "commonfunc.h"
#include <dirent.h>


#define INET_NUM 8888   //埠號
#define IP_ADDRESS "10.104.6.110"   //伺服器IP


#ifndef BUFF_LEN
#define BUFF_LEN 1028
#endif


#define CMD_LS         11//列出客戶端所有檔案
#define CMD_SERVER_LS  22//列出伺服器所有檔案
#define CMD_DOWNLOAD   33//下載檔案
#define CMD_UPLOAD     44//上傳檔案
#define CMD_QUIT       55//退出
#define CMD_ERROR      -1//錯誤




int main(void)
{
    int sockfd;
    DIR* dp;
    struct dirent *ep;
    sockfd = network_init(NET_CLIENT, IP_ADDRESS, INET_NUM);   //初始化網路連線
    if(sockfd == -1)
    {
        perror("Network init error!");
        exit(EXIT_FAILURE);
    }
    printf("Connect success!\n\n");
    printf("*********Operation Help*********\n");   //操作幫助
    printf("顯示本地檔案列表:        ls\n");
    printf("顯示伺服器檔案列表:      server ls\n");
    printf("實現xxx檔案讀取與上傳:   upload xxx\n");
    printf("實現xxx檔案下載與儲存:   download xxx\n");
    printf("斷開socket連結:          quit\n");
    printf("*************************************\n\n");


    char SEND_BUFF[BUFF_LEN];   //傳送資料緩衝區
    int cmd_result;   //存放命令解析結果
    while(1)
    {
        fgets(SEND_BUFF, sizeof(SEND_BUFF), stdin);   //輸入命令
        SEND_BUFF[strlen(SEND_BUFF)-1] = '\0';   //去掉最後輸入的回車符
        
        cmd_result = ftp_cmd_analyse(SEND_BUFF);   //解析輸入的指令
        switch(cmd_result)
        {
            case CMD_ERROR:
                printf("ERROR!\n");
                break;
           
             ////////////////////////////////////////////////////////////
            case CMD_LS:    //列出本地檔案列表
                 dp=opendir (".");
                 printf("*********File List of Client*********\n");
                while((ep = readdir(dp)))
                      {
                     if(ep->d_name[0] != '.')
                            {
                          printf("%s\n",ep->d_name);
                            }
                      }
                printf("*************************************\n");  
                printf("List file success!\n");      
               closedir(dp);
               break;
           ///////////////////////////////////////////////////////////
            case CMD_SERVER_LS:   //列出伺服器端可下載的所有檔案
                printf("*********File List of Server*********\n");
                if(ftp_getlist(sockfd) == -1)
                {
                    printf("List file error!\n");
                }
                else
                {          
                printf("*************************************\n");
                printf("List file success!\n");
                }
                break;
           
             ////////////////////////////////////////////////////////////
            case CMD_DOWNLOAD:   //從伺服器下載檔案
                if(write(sockfd, SEND_BUFF, BUFF_LEN) == -1)
                {
                    perror("Send cmd error!");
                    break;
                }
                if(ftp_getfile(sockfd, SEND_BUFF+9) == -1)   //下載檔案
                {
                    printf("Download error!\n");
                }
                else
                {
                 printf("Download The File Success!!\n");
                }
                break;
            ////////////////////////////////////////////////////////////
            case CMD_UPLOAD:   //上傳檔案到伺服器   
                 
                if(write(sockfd, SEND_BUFF, BUFF_LEN) == -1)
                {
                    perror("Send cmd error!");
                    break;
                }
                if(ftp_putfile(sockfd, SEND_BUFF+7) == -1)//上傳檔案
                {
                    printf("Upload error!\n");
                }
                else
                {
                    printf("Upload The File Success!!\n");
                }
                break; 
            ////////////////////////////////////////////////////////////
            case CMD_QUIT:   //斷開連線
                printf("Welcome to use again!\nQUIT!\n");
                close(sockfd);//客戶端關閉檔案描述符後就會自動斷開連線
                exit(EXIT_SUCCESS);
                break;
            ////////////////////////////////////////////////////////////
            default:
                break;
        }
    }
    close(sockfd);//客戶端關閉檔案描述符後就會自動斷開連線
    return 0;
}

//////////////////////////////////////////////////////////////////////伺服器程式///////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "commonfunc.h"


#define INET_NUM 8888   //埠號
#define IP_ADDRESS "10.104.6.110"   //IP地址


#define BUFF_LEN 1028


#define CMD_LS          11   //列出客戶端所有檔案
#define CMD_SERVER_LS   22   //列出伺服器所有檔案
#define CMD_DOWNLOAD    33   //下載檔案
#define CMD_UPLOAD      44   //上傳檔案
#define CMD_QUIT        55   //退出
#define CMD_ERROR       -1   //錯誤




int main(void)
{
    int sockfd;   //網路套接字
    sockfd = network_init(NET_SERVER, IP_ADDRESS, INET_NUM);   //初始化網路連線
    if(sockfd == -1)
    {
        perror("Network init error!");
        exit(EXIT_FAILURE);
    }
    printf("LISTEN-ing...\n");


    char RCV_BUFF[BUFF_LEN];   //接收快取
    char SEND_BUFF[BUFF_LEN];  //傳送快取
    int cmd_result;   //命令解析結果
    int connectfd;   //建立連線後用於通訊的套接字檔案
    int readlen;   //讀取到的位元組數
    while(1)
    {
        if((connectfd = accept(sockfd, NULL, NULL)) == -1)   //連結出錯,給出提示
        {
            perror("Connect error!\n");
            break;
        }
        printf("Connect success!\n");
        while(1)
        {
            readlen = read(connectfd, RCV_BUFF, sizeof(RCV_BUFF));   //接收命令
            if(readlen <0)//接收出錯
            {
                perror("Read error!\n");
                break;
            }
            else
            {
                if(readlen == 0)   //客戶端關閉檔案描述符後就會斷開連線
                {
                    printf("Welcome to use again!\nQUIT...\n");
                    break;
                }
                else
                {
                    printf("**************************\n");
                    printf("RECV:%s\n",RCV_BUFF);
                    cmd_result = ftp_cmd_analyse(RCV_BUFF);   //解析命令
               switch(cmd_result)
                    {
                        case CMD_ERROR:
                            printf("CMD_ERROR!\n");
                            break;
                        case CMD_SERVER_LS:  //檢視伺服器檔案列表
                            if(ftp_putlist(connectfd) == -1)
                            {
                                printf("List files error!\n");
                            }
                            else
                            {
                                printf("List files success!\n");
                            }
                            break;
                        case CMD_DOWNLOAD:   //客戶端從伺服器下載檔案
                            printf("Put files:%s\n", RCV_BUFF+9);
                            if(ftp_putfile(connectfd, RCV_BUFF+9) == -1)
                            {
                                printf("Put files error!\n");
                            }
                            else
                            {
                                printf("Put files success!\n");
                            }
                            break;
                        case CMD_UPLOAD:   //客戶端上傳檔案到伺服器
                            printf("Get files:%s\n", RCV_BUFF+7);
                            if(ftp_getfile(connectfd, RCV_BUFF+7) == -1)
                            {


                                printf("Get files error!\n");
                            }
                            else
                            {
                                printf("Get files success!\n");
                            }
                            break;
                        default:
                            break;
                    }
                }
            }
        }
        close(connectfd);//客戶端退出,斷開連線
    }
    close(sockfd);
    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////