项目需求

  1. 编写客户端和服务器
  2. 客户端可以查看服务器端目录中的文件名
  3. 客户端可以从服务器中下载文件
  4. 客户端可以上传文件给服务器

开发步骤

1.流程图(一个大致的思路和想法)

2.根据流程图写框架(像做填空题一样,先写出题目,后去填充)

3.逐步实现每个功能(会发现流程图中的错误,回过头去补充完善)

FTP服务器

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#define ERR_LOG(msg) do{\
    perror(msg);\
    printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define FILELEN 20
#define DATALEN 512
typedef struct protocol
{
    char type;
    char file[FILELEN];
    char data[DATALEN];
}PROTOC;
int main(int argc, const char *argv[])
{
    if(argc < 3)
    {
        printf("请输入本地IP号和端口号\n");
        return 0;
    }
    //1.流式套接字socket
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    if(sfd < 0)
    {
        ERR_LOG("socket");
        return -1;
    }
    //端口快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int))<0)
    {
        ERR_LOG("setsockopt");
        return -1;
    }
    //2.bind绑定本地IP和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(*(unsigned short*)argv[2]);
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    if(bind(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
    {
        ERR_LOG("bind");
        return -1;
    }
    //3.listen
    if(listen(sfd,5) < 0)
    {
        ERR_LOG("listen");
        return -1;
    }
    printf("开始监听\n");
    //变量定义
    int option;
    PROTOC buf = {0,{0},{0}};
    int recv_len = 0;
    char pathfile[FILELEN] = "";
    while(1)
    {
        //4.accept
        struct sockaddr_in caddr;
        socklen_t caddr_len = sizeof(caddr);
        int cfd = accept(sfd,(struct sockaddr*)&caddr,&caddr_len);
        if(cfd < 0)
        {
            ERR_LOG("accept");
            return -1;
        }
        printf("连接成功\n");
        while(1)
        {
            recv_len = recv(cfd,&buf,sizeof(buf),0);
            if(recv_len < 0)
            {
                ERR_LOG("recv");
                return -1;
            }
            else if(0 == recv_len)
            {
                printf("ftp客户端退出\n");
                break;
            }
            switch(buf.type)
            {
                case 'D'://下载请求
                    printf("收到下载文件的请求\n");
                    //获取文件目录列表
                    bzero(buf.data,DATALEN);
                    DIR* dp = opendir("./");
                    if(NULL == dp)
                    {
                        ERR_LOG("opendir");
                        return -1;
                    }
                    struct dirent *readptr = NULL;
                    while(1)
                    {
                        readptr = readdir(dp);
                        if(NULL == readptr)
                        {
                            printf("目录读取完毕\n");
                            break;
                        }
                        if(readptr->d_name[0] != '.')
                        {
                            strcat(buf.data,readptr->d_name);
                            strcat(buf.data,"\n");
                        }
                    }
                    closedir(dp);
                    //发送文件目录列表
                    buf.type = 'L';
                    buf.data[strlen(buf.data)-1] = 0;
                    if(send(cfd,&buf,sizeof(buf),0) < 0)
                    {
                        ERR_LOG("send");
                        return -1;
                    }
                    printf("目录列表发送完毕\n");
                    //等待接收要下载的文件名
                    if(recv(cfd,&buf,sizeof(buf),0) < 0)
                    {
                        ERR_LOG("recv");
                        return -1;
                    }
                    if('F' == buf.type)//接收到要下载的文件名
                    {
                        strcpy(pathfile,"./");
                        strcat(pathfile,buf.file);
                        pathfile[strlen(pathfile)-1] = 0;
                        //printf("%d\n", strlen(pathfile));//text
                        //printf("%s", pathfile);//text
                    }
                    //打开文件
                    int fd = open(pathfile,O_RDONLY);
                    if(fd < 0)
                    {
                        ERR_LOG("open");
                        return -1;
                    }                
                    int read_len;
                    while(1)//读&发 文件数据
                    {
                        //读取文件数据
                        bzero(buf.data,DATALEN);
                        read_len = read(fd,buf.data,DATALEN);
                        if(read_len < 0)
                        {
                            ERR_LOG("read_len");
                            return -1;
                        }
                        else if(0 == read_len)//读取完毕
                        {
                            buf.type = 'O';
                            if(send(cfd,&buf,sizeof(buf),0) < 0)
                            {
                                ERR_LOG("send");
                                return -1;
                            }
                            printf(">>>>文件%s发送成功\n",pathfile+2);
                            break;            
                        }
                        //发送文件数据
                        buf.type = 'W';
                        if(send(cfd,&buf,sizeof(buf),0) < 0)
                        {
                            ERR_LOG("send");
                            return -1;
                        }                
                    }
                    close(fd);
                    break;
                case 'U'://上传请求//接收客户端发来的文件名
                    printf("收到上传文件的请求\n");
                    strcpy(pathfile,"./");
                    strcat(pathfile,buf.file);
                    pathfile[strlen(pathfile)-1] = 0;
                    //创建文件
                    int fd1 = open(pathfile,O_WRONLY|O_CREAT|O_TRUNC,0664);
                    if(fd1 < 0)
                    {
                        ERR_LOG("open");
                        return -1;
                    }
                    while(1)//收&写 文件数据
                    {
                        //等待接收下载数据
                        if(recv(cfd,&buf,sizeof(buf),0) < 0)
                        {
                            ERR_LOG("recv");
                            return -1;
                        }
                        if('W' == buf.type)
                        {
                            //写入文件
                            if(write(fd1,buf.data,strlen(buf.data)) < 0)
                            {
                                ERR_LOG("write");
                                return -1;
                            }
                        }
                        else if('O' == buf.type)
                        {
                            printf(">>>>文件%s接收成功\n",pathfile+2);
                            break;
                        }                        
                    }
                    close(fd1);
                    break;
                default:
                    printf("收到无效的请求指令\n");
                    break;
            }
        }
        //close文件描述符
        close(cfd);
    }
    //close文件描述符
    close(sfd);
    return 0;
}

FTP客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define ERR_LOG(msg) do{\
    perror(msg);\
    printf("%d %s %s\n",__LINE__,__func__,__FILE__);\
}while(0)
#define FILELEN 20
#define DATALEN 512
typedef struct protocol
{
    char type;
    char file[FILELEN];
    char data[DATALEN];
}PROTOC;
int main(int argc, const char *argv[])
{
    if(argc < 3)
    {
        printf("请输入服务器IP号和端口号\n");
        return 0;
    }
    //1.流式套接字socket
    int sfd = socket(AF_INET,SOCK_STREAM,0);
    if(sfd < 0)
    {
        ERR_LOG("socket");
        return -1;
    }
    //2.connect连接服务器
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(*(unsigned short*)argv[2]);
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    if(connect(sfd,(struct sockaddr*)&saddr,sizeof(saddr)) < 0)
    {
        ERR_LOG("connect");
        return -1;
    }
    printf("连接成功\n");
    //变量定义
    int option;
    PROTOC buf = {0,{0},{0}};
    char pathfile[FILELEN] = "";
    char inputfile[FILELEN] = "";
    while(1)
    {
        printf("******FTP*******\n");
        printf("*****1.下载*****\n");
        printf("*****2.上传*****\n");
        printf("*****3.退出*****\n");
        printf("****************\n");
        printf("请输入选项--->");
        scanf("%d",&option);
        getchar();
        switch(option)
        {
            case 1://下载
                //获取服务端文件列表
                //发送下载请求给服务器
                buf.type = 'D';//下载请求
                if(send(sfd,&buf,sizeof(buf),0) < 0)
                {
                    ERR_LOG("send");
                    return -1;
                }
                //等待接收文件目录列表
                if(recv(sfd,&buf,sizeof(buf),0) < 0)
                {
                    ERR_LOG("recv");
                    return -1;
                }
                if('L' == buf.type)
                {
                    printf("*****文件列表*****\n");
                    printf("%s\n",buf.data);//打印文件目录列表
                    printf("*****文件列表*****\n");
                    printf("请输入要下载的文件名--->");    
                }
                //发送要下载的文件名给服务器
                bzero(inputfile,FILELEN);
                fgets(inputfile,FILELEN,stdin);
                buf.type = 'F';//Filename
                bzero(buf.file,FILELEN);
                strcpy(buf.file,inputfile);
                if(send(sfd,&buf,sizeof(buf),0) < 0)
                {
                    ERR_LOG("send");
                    return -1;
                }
                //打开(创建)文件
                strcpy(pathfile,"./");
                strcat(pathfile,inputfile);
                pathfile[strlen(pathfile)-1] = 0;
                //printf("%d\n", strlen(pathfile));//text
                //printf("%s", pathfile);//text
                int fd = open(pathfile,O_WRONLY|O_CREAT|O_TRUNC,0664);
                if(fd < 0)
                {
                    ERR_LOG("open");
                    return -1;
                }
                while(1)//收&写 文件数据
                {
                    //等待接收下载数据
                    if(recv(sfd,&buf,sizeof(buf),0) < 0)
                    {
                        ERR_LOG("recv");
                        return -1;
                    }
                    if('W' == buf.type)
                    {
                        //写入文件
                        if(write(fd,buf.data,strlen(buf.data)) < 0)
                        {
                            ERR_LOG("write");
                            return -1;
                        }
                    }
                    else if('O' == buf.type)
                    {
                        printf(">>>>>>>>>文件%s下载成功<<<<<<<<<\n",pathfile+2);
                        break;
                    }    
                }
                close(fd);
                break;
            case 2://上传
REINPUT:
                printf("请输入要上传的文件名--->");
                bzero(inputfile,FILELEN);
                fgets(inputfile,FILELEN,stdin);
                //打开文件
                strcpy(pathfile,"./");
                strcat(pathfile,inputfile);
                pathfile[strlen(pathfile)-1] = 0;
                int fd1 = open(pathfile,O_RDONLY);
                if(fd1 < 0)
                {
                    printf("当前目录没有这个文件\n");
                    goto REINPUT;
                }
                buf.type = 'U';//Upload Filename
                bzero(buf.file,FILELEN);
                strcpy(buf.file,inputfile);
                if(send(sfd,&buf,sizeof(buf),0) < 0)
                {
                    ERR_LOG("send");
                    return -1;
                }
                int read_len;
                while(1)//读&发 文件数据
                {
                    //读取文件数据
                    bzero(buf.data,DATALEN);
                    read_len = read(fd1,buf.data,DATALEN);
                    if(read_len < 0)
                    {
                        ERR_LOG("open");
                        return -1;            
                    }
                    else if(0 == read_len)//读取完毕
                    {
                        buf.type = 'O';
                        if(send(sfd,&buf,sizeof(buf),0) < 0)
                        {
                            ERR_LOG("send");
                            return -1;
                        }
                        printf(">>>>>>>>>文件%s上传成功<<<<<<<<<\n",pathfile+2);
                        break;
                    }
                    //发送文件数据
                    buf.type = 'W';
                    if(send(sfd,&buf,sizeof(buf),0) < 0)
                    {
                        ERR_LOG("send");
                        return -1;
                    }
                }
                close(fd1);
                break;
            case 3://退出
                close(sfd);
                exit(1);
                break;
            default:
                buf.type = 'e';
                if(send(sfd,&buf,sizeof(buf),0) < 0)
                {
                    ERR_LOG("send");
                    return -1;
                }
                printf("请输入正确的选项\n");
                break;                                
        }
    }
    //close文件描述符
    close(sfd);
    return 0;
}

演示

Last modification:2021 年 04 月 19 日 11 : 01 : 36