zoukankan      html  css  js  c++  java
  • Web服务器文件传输程序客户端程序实现

     

    1、 客户端程序--主函数

    客户端主程序的流程图如下:

     

    主程序主要是分析输入的命令,根据不同命令调用不同的函数处理或者进行出错处理,函数代码如下:

    #include "common.h"
    
    int main(void)
    
    {
    
    char cline[COMMAND_LINE];             /* 缓冲区,存储用户输入的命令 */
    
    struct command_line command; /* 命令结构,存储分解后的命令 */
    
    int sock_fd;
    
    struct sockaddr_in sin;                /* 服务器端的地址结构 */
    
    printf("myftp$: ");                       /* 打印提示符 */
    
    fflush(stdout);                             /* 保证提示符显示 */
    
    while(fgets(cline, MAX_LINE, stdin) != NULL){ /* 得到一行命令 */
    
            if(split(command, cline) == -1) /* 将命令拆分为命令名和命令参数的形式*/
    
                   exit(1);
    
            /* 根据命令名作命令处理,比较命令名和每一个合法命令,匹配则处理 */
    
            if(strcasecmp(command.name, "get") == 0){ /* get命令 */
    
                   if(do_get(command.argv[1], command.argv[2], sock_fd) == -1)
    
                          exit(1); /* 命令处理出错则退出 */
    
            }else if(strcasecmp(command.name, "put") == 0){ /* put命令 */
    
                   if(do_put(command.argv[1], command.argv[2], sock_fd) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "cd") == 0){ /* cd命令 */
    
                   if(do_cd(command.argv[1]) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "!cd") == 0){ /* !cd命令 */
    
                   if(do_serv_cd(command.argv[1], sock_fd) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "ls") == 0){ /* ls命令 */
    
                   if(do_ls(command.argv[1]) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "!ls") == 0){ /* !ls命令 */
    
                   if(do_serv_ls(command.argv[1], sock_fd) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "connect") == 0){ /* connect               命令 */
    
                   if(do_connect(command.argv[1], &sin, &sock_fd) == -1)
    
                          exit(1);
    
            }else if(strcasecmp(command.name, "bye") == 0){ /* bye命令 */
    
                   if(do_bye(sock_fd) == -1)
    
                          exit(1);
    
                   break; /* 向服务器端发送退出信息后,则跳出循环,退出程序 */
    
            }else{ /* 错误命令,打印程序的用法 */
    
                   printf("wrong command
    ");
    
                   printf("usage : command arg1, arg2, ... argn
    ");
    
            }
    
            printf("myftp$: "); /* 再次打印提示符,准备接受新的命令 */
    
            fflush(stdout);
    
    }
    
    if(close(sock_fd) == -1){ /* 执行bye命令后执行到这里,关闭通信用套接字 */
    
            perror("fail to close");
    
            exit(1);
    
    }
    
    return 0; /* 程序正常退出 */
    
    }

    2、客户端程序--命令拆分

    命令拆分程序可以根据之前的程序设计来进行编写,先判断是什么命令,再根据命令类型的不同调用不同的函数,将命令中的参数分离出来作为。主程序中调用该程序,根据不同的命令调用不同的函数,并且将参数传递给处理函数,流程图如下:

     

    程序代码如下:

    #include "common.h"
    
    /*下面的宏将cline串中从pos所表示的位置开始,跳过连续的空格和制表符 */
    
    #define del_blank(pos, cline); { 
    
    while(cline[pos] != '' && (cline[pos] == ' ' || cline[pos] == '	'))
    
    { 
    
            pos++; 
    
    } 
    
    }
    
    /* 下面的宏得到一个命令参数。
    
    * 将cline串中从pos所表示的位置的内容复制到arg缓冲区中,直到遇到空格、制表符或者结束符为止
    
    */
    
    #define get_arg(arg, pos, cline); { 
    
    int i = 0; 
    
    while(cline[pos] != '' && cline[pos] != ' '&& cline[pos] != '/t'){ 
    
            arg[i++] = com[pos++]; 
    
    } 
    
    }
    
    /* 将一个命令字符串分割为命令参数并存储在command_struct结构中
    
     * 成功则返回拆分后的命令参数的个数,失败返回-1
    
    * command : 存储命令和命令参数的结构体
    
    * cline : 命令字符串
    
    */
    
    int split(struct command_line * command, char cline[ ])
    
    {
    
    int i;
    
    int pos = 0;
    
    clien[strlen(cline) - 1] = ''; /* 得到命令字符串的长度,将最后一个‘
    ’                                                               替换为‘’ */
    
    del_blank(pos, cline);                    /* 过滤空格,直到遇到第一个参数 */
    
    i = 0;
    
    while(cline[pos] != ''){ /* 处理整个命令字符串 */
    
            /* 为存储命令参数的数组分配空间 */
    
            if((command->argv[i] = (char *)malloc(MAX_LENGTH)) == NULL){
    
                   perror("fail to malloc");
    
                   return -1;
    
            }
    
            /* 得到一个参数,将两个空格之间的内容复制到存储参数的数组 */
    
            get_arg(command->argv[i], pos, cline);
    
            i++;                                       /* 下一个参数 */
    
            del_blank(pos, cline);     /* 过滤掉空格 */
    
    }
    
    command->argv[i] = NULL;          /* 命令参数数组以NULL结尾 */
    
    comand->name = command->.argv[0]; /* 命令名和第一个命令参数实际上指向同一块内存区域 */
    
    return i;
    
    }

    3、客户端程序—命令模块处理

    命令处理模块是这个程序的核心模块,客户端的命令处理程序有7个。

    (1)do_connect()函数

    do_connect()函数完成了创建套接字并且连接的工作,这个函数返回说明客户端已经和服务器成功连接。do_connect()函数处理connect命令,成功返回0,失败返回-1。connect命令的形式为:connect arg1,参数 arg1是服务器的IP地址。

    参数说明:

    ip : 字符指针,指向服务器的地址,是一个十进制点分字符串;

    sin : 地质结构指针,指向服务器地址结构,在connect函数中填充;

    sock_fd : 整型指针,指向通信用套接字描述符,该套接字在connct函数中设置,将描述符传回。

    int do_connect(char *ip,struct sockaddr_in *sin, int *sock_fd)
    
    {
    
    int sfd; /* 临时的套接字描述符 */
    
    bzero(&sin, sizeof(struct sockaddr_in)); /* 将地址结构清空 */
    
    sin.sin_family = AF_INET;                             /* 使用IPv4地址族 */
    
    /* 将点分十进制形式的IP地址转换成为二进制形式的地址,并且存储在地址结构中 */
    
    if(inet_pton(AF_INET, ip, &sin.sin_addr) == -1){
    
            perror("wrong format of ip address"); /* 如果地址格式为非法则出错 */
    
            return -1;
    
    }
    
    sin.sin_port = htons(PORT); /* 将端口号转换成为网络字节序存储在地址结构中 */
    
    if(sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){ /* 创建套接字 */
    
            perror("fail to creat socket");
    
            return -1;      
    
    }
    
    /* 使用该套接字,和填充好的地址结构进行连接 */
    
    if(connect(sfd, (struct sockaddr *)sin, sizeof(struct sockaddr_in)) ==     -1){
    
            perror("fail to connect");
    
            return -1;
    
    }
    
    *sock_fd = sfd; /* 将sfd保存的套接字的描述符赋值给sock_fd所指向区域返回 */
    
    return 0;
    
    }

    (2)do_get函数处理get命令,成功返回0,失败返回-1。get命令的形式为:get arg1 arg2。该命令从服务器端取得文件,arg1表示源文件,arg2表示目的路径,如果该文件已存在则覆盖。

    参数说明:

    src : 源文件,是一个绝对路径

    dst : 目的目录,是一个绝对的路径

    sock_fd : 通信用的套接字描述符

    int do_get(const char *src, const char *dst, int sock_fd)
    
    {
    
    char *dst_file;        /* 目的路径 */
    
    char *p;
    
    struct stat statbuf;/* 文件状态缓冲区 */
    
    int n, fd;
    
    char buf[MAX_LINE];/* 命令缓冲区,存储发送给服务器端的命令 */
    
    int len;
    
    int res = -1;          /* 返回值默认的值为-1 */
    
    if(src == NULL || dst == NULL){/* 检查源文件和目的地址是不是空串 */
    
            printf(wrong command
    );  /* 是空串则报错 */
    
            return -1;
    
    }
    
    /* 如果源文件路径的最后一个字符是‘/’,则说明源文件不是一个普通文件,而是一个目录   */
    
      if(src[strlen(src)-1]=='/'){
    
            printf("source file should be a regular file
    ");
    
            return -1;
    
    }
    
    /* 为最终的目的文件路径分配内存空间
    
    * 文件路径由目标目录和源文件的文件名组成
    
    * 所以该缓冲区的大小最大是两个文件路径的长
    
    */
    
      if( (dst_file = (char *)malloc(strlen(dst) + strlen(src))) == NULL){
    
            perror("fail to malloc");
    
            return -1;                       /* 如果内存分配不成功则返回-1 */
    
      }
    
      strcpy(dst_file, dst);         /* 将目标目录复制到dst_file缓冲区中 */
    
      /* 如果目标目录的结尾不是‘/’,则添加‘/’,例如/home/admin—>/home/admin/ */
    
      if(dst_file[strlen(dst_file) - 1] != '/')
    
            strcat(dst_file, "/");
    
      p = rindex(src, '/');              /* 取源文件路径中最后一个‘/’,其后面是文件名 */
    
      strcat(dst_file, p + 1);    /* 跳过‘/’,得到源文件文件名 */
    
    /* 如果目标文件不存在则创建该文件,使用权限字“0644”
    
    * 该权限表示所有者拥有读写权限而组用户和其他用户只有读权限
    
    * 如果该文件存在,则将文件截短为0打开,覆盖原文件
    
    */
    
      if((fd = open(dst_file, O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1){
    
            perror("fail to open dst-file");
    
            goto end2; /* 打开文件失败,退出时只需要释放dst_file的内存,而不需要关闭            文件 */
    
      }
    
      if(fstat(fd, &statbuf) == -1){ /* 取目标文件的文件状态 */
    
            perror("fail to stat dst-file");
    
            goto end1;
    
      }
    
    /* 如果目标文件已经存在,且不是一个普通文件,则无法传输
    
    * 因为这样会造成将已经存在的目录等其他特殊文件覆盖
    
    */
    
      if(!S_ISREG(statbuf.st_mode)){
    
            printf("dst-file should be a regular file");
    
            goto end1;
    
      }
    
        sprintf(buf, "GET %s", src); /* 准备向服务器端发送GET命令 */
    
      if(my_write(sock_fd, buf, strlen(buf)+1) == -1) /* 发出命令 */
    
            goto end1;                                                               /* 出错则退出 */
    
      if( (n = my_read(sock_fd, buf, MAX_LINE)) <= 0){ /* 接收服务器端发回的确                                                                                       认信息 */
    
            goto end1;
    
      }
    
      if(buf[0] == 'E'){ /* 如果收到的信息是ERR,表示出错 */
    
            write(STDOUT_FILENO, buf, n); /* 向屏幕输出服务器发来的出错信息 */
    
            res = 0;
    
            goto end1;
    
      }
    
    /* 如果对没有出错,服务器反馈的信息格式为“OK 请求文件的文件大小”
    
    * 跳过字符串“OK”和一个空格,从第三个字符开始,取得文件的长度
    
    */
    
      len = atoi(&buf[3]);
    
    /* 向服务器发出准备好的信息,服务器接收到此信息后开始传送文件的内容 */
    
      if(my_write(sock_fd, "RDY", 3) == -1)
    
            goto end1;
    
      while(1){ /* 循环读,直到读完所有的文件内容 */
    
            n = my_read(sock_fd, buf, MAX_LINE); /* 读服务器传送来的内容 */
    
            if(n > 0){ /* 读到的字节数大于0,说明正常 */
    
                   write(fd, buf, n); /* 将读到的内容写到打开的目标文件中去 */
    
                   len -= n; /* 文件总长度减少 */
    
            }else if(len == 0){ /* 读到的字节数等于0,说明通信已经结束 */
    
                   /* 如果请求的文件已经读完,则正常结束此次命令的执行 */
    
                   printf("OK
    ");
    
                   break;
    
            }else /* 读到的字节数小于0,出错 */
    
                   goto end1;
    
    }
    
      res = 0; /* 运行到此,则函数正常返回,返回值为0 */
    
    end1:
    
      free(dst_file); /* 释放动态分配的内存空间 */
    
    end2:
    
      close(fd); /* 关闭文件 */
    
    return res;
    
    }
  • 相关阅读:
    linux expr命令执行问题
    Virtual Box设置Host only模式的网络互通问题
    Linux下管理软件的方法
    转载 AMI方案和Insyde方案
    笔记二(名词详解)持续更新。。。
    笔记一(固件、BIOS、UEFI)
    Cache As Ram
    (二十四)C语言之文件
    (二十三)C语言之位运算
    (二十二)C语言之typedef
  • 原文地址:https://www.cnblogs.com/Mr--Yang/p/6853684.html
Copyright © 2011-2022 走看看