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

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

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

  1. #include "common.h"
  2.  
  3. int main(void)
  4.  
  5. {
  6.  
  7. char cline[COMMAND_LINE]; /* 缓冲区,存储用户输入的命令 */
  8.  
  9. struct command_line command; /* 命令结构,存储分解后的命令 */
  10.  
  11. int sock_fd;
  12.  
  13. struct sockaddr_in sin; /* 服务器端的地址结构 */
  14.  
  15. printf("myftp$: "); /* 打印提示符 */
  16.  
  17. fflush(stdout); /* 保证提示符显示 */
  18.  
  19. while(fgets(cline, MAX_LINE, stdin) != NULL){ /* 得到一行命令 */
  20.  
  21. if(split(command, cline) == -) /* 将命令拆分为命令名和命令参数的形式*/
  22.  
  23. exit();
  24.  
  25. /* 根据命令名作命令处理,比较命令名和每一个合法命令,匹配则处理 */
  26.  
  27. if(strcasecmp(command.name, "get") == ){ /* get命令 */
  28.  
  29. if(do_get(command.argv[], command.argv[], sock_fd) == -)
  30.  
  31. exit(); /* 命令处理出错则退出 */
  32.  
  33. }else if(strcasecmp(command.name, "put") == ){ /* put命令 */
  34.  
  35. if(do_put(command.argv[], command.argv[], sock_fd) == -)
  36.  
  37. exit();
  38.  
  39. }else if(strcasecmp(command.name, "cd") == ){ /* cd命令 */
  40.  
  41. if(do_cd(command.argv[]) == -)
  42.  
  43. exit();
  44.  
  45. }else if(strcasecmp(command.name, "!cd") == ){ /* !cd命令 */
  46.  
  47. if(do_serv_cd(command.argv[], sock_fd) == -)
  48.  
  49. exit();
  50.  
  51. }else if(strcasecmp(command.name, "ls") == ){ /* ls命令 */
  52.  
  53. if(do_ls(command.argv[]) == -)
  54.  
  55. exit();
  56.  
  57. }else if(strcasecmp(command.name, "!ls") == ){ /* !ls命令 */
  58.  
  59. if(do_serv_ls(command.argv[], sock_fd) == -)
  60.  
  61. exit();
  62.  
  63. }else if(strcasecmp(command.name, "connect") == ){ /* connect 命令 */
  64.  
  65. if(do_connect(command.argv[], &sin, &sock_fd) == -)
  66.  
  67. exit();
  68.  
  69. }else if(strcasecmp(command.name, "bye") == ){ /* bye命令 */
  70.  
  71. if(do_bye(sock_fd) == -)
  72.  
  73. exit();
  74.  
  75. break; /* 向服务器端发送退出信息后,则跳出循环,退出程序 */
  76.  
  77. }else{ /* 错误命令,打印程序的用法 */
  78.  
  79. printf("wrong command\n");
  80.  
  81. printf("usage : command arg1, arg2, ... argn\n");
  82.  
  83. }
  84.  
  85. printf("myftp$: "); /* 再次打印提示符,准备接受新的命令 */
  86.  
  87. fflush(stdout);
  88.  
  89. }
  90.  
  91. if(close(sock_fd) == -){ /* 执行bye命令后执行到这里,关闭通信用套接字 */
  92.  
  93. perror("fail to close");
  94.  
  95. exit();
  96.  
  97. }
  98.  
  99. return ; /* 程序正常退出 */
  100.  
  101. }

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

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

程序代码如下:

  1. #include "common.h"
  2.  
  3. /*下面的宏将cline串中从pos所表示的位置开始,跳过连续的空格和制表符 */
  4.  
  5. #define del_blank(pos, cline); { \
  6.  
  7. while(cline[pos] != '\0' && (cline[pos] == ' ' || cline[pos] == '\t'))\
  8.  
  9. { \
  10.  
  11. pos++; \
  12.  
  13. } \
  14.  
  15. }
  16.  
  17. /* 下面的宏得到一个命令参数。
  18.  
  19. * 将cline串中从pos所表示的位置的内容复制到arg缓冲区中,直到遇到空格、制表符或者结束符为止
  20.  
  21. */
  22.  
  23. #define get_arg(arg, pos, cline); { \
  24.  
  25. int i = ; \
  26.  
  27. while(cline[pos] != '\0' && cline[pos] != ' '&& cline[pos] != '/t'){ \
  28.  
  29. arg[i++] = com[pos++]; \
  30.  
  31. } \
  32.  
  33. }
  34.  
  35. /* 将一个命令字符串分割为命令参数并存储在command_struct结构中
  36.  
  37. * 成功则返回拆分后的命令参数的个数,失败返回-1
  38.  
  39. * command : 存储命令和命令参数的结构体
  40.  
  41. * cline : 命令字符串
  42.  
  43. */
  44.  
  45. int split(struct command_line * command, char cline[ ])
  46.  
  47. {
  48.  
  49. int i;
  50.  
  51. int pos = ;
  52.  
  53. clien[strlen(cline) - ] = '\0'; /* 得到命令字符串的长度,将最后一个‘\n’ 替换为‘\0’ */
  54.  
  55. del_blank(pos, cline); /* 过滤空格,直到遇到第一个参数 */
  56.  
  57. i = ;
  58.  
  59. while(cline[pos] != '\0'){ /* 处理整个命令字符串 */
  60.  
  61. /* 为存储命令参数的数组分配空间 */
  62.  
  63. if((command->argv[i] = (char *)malloc(MAX_LENGTH)) == NULL){
  64.  
  65. perror("fail to malloc");
  66.  
  67. return -;
  68.  
  69. }
  70.  
  71. /* 得到一个参数,将两个空格之间的内容复制到存储参数的数组 */
  72.  
  73. get_arg(command->argv[i], pos, cline);
  74.  
  75. i++; /* 下一个参数 */
  76.  
  77. del_blank(pos, cline); /* 过滤掉空格 */
  78.  
  79. }
  80.  
  81. command->argv[i] = NULL; /* 命令参数数组以NULL结尾 */
  82.  
  83. comand->name = command->.argv[]; /* 命令名和第一个命令参数实际上指向同一块内存区域 */
  84.  
  85. return i;
  86.  
  87. }

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

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

(1)do_connect()函数

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

参数说明:

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

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

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

  1. int do_connect(char *ip,struct sockaddr_in *sin, int *sock_fd)
  2.  
  3. {
  4.  
  5. int sfd; /* 临时的套接字描述符 */
  6.  
  7. bzero(&sin, sizeof(struct sockaddr_in)); /* 将地址结构清空 */
  8.  
  9. sin.sin_family = AF_INET; /* 使用IPv4地址族 */
  10.  
  11. /* 将点分十进制形式的IP地址转换成为二进制形式的地址,并且存储在地址结构中 */
  12.  
  13. if(inet_pton(AF_INET, ip, &sin.sin_addr) == -){
  14.  
  15. perror("wrong format of ip address"); /* 如果地址格式为非法则出错 */
  16.  
  17. return -;
  18.  
  19. }
  20.  
  21. sin.sin_port = htons(PORT); /* 将端口号转换成为网络字节序存储在地址结构中 */
  22.  
  23. if(sfd = socket(AF_INET, SOCK_STREAM, )) == -){ /* 创建套接字 */
  24.  
  25. perror("fail to creat socket");
  26.  
  27. return -;
  28.  
  29. }
  30.  
  31. /* 使用该套接字,和填充好的地址结构进行连接 */
  32.  
  33. if(connect(sfd, (struct sockaddr *)sin, sizeof(struct sockaddr_in)) == -){
  34.  
  35. perror("fail to connect");
  36.  
  37. return -;
  38.  
  39. }
  40.  
  41. *sock_fd = sfd; /* 将sfd保存的套接字的描述符赋值给sock_fd所指向区域返回 */
  42.  
  43. return ;
  44.  
  45. }

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

参数说明:

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

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

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

  1. int do_get(const char *src, const char *dst, int sock_fd)
  2.  
  3. {
  4.  
  5. char *dst_file; /* 目的路径 */
  6.  
  7. char *p;
  8.  
  9. struct stat statbuf;/* 文件状态缓冲区 */
  10.  
  11. int n, fd;
  12.  
  13. char buf[MAX_LINE];/* 命令缓冲区,存储发送给服务器端的命令 */
  14.  
  15. int len;
  16.  
  17. int res = -; /* 返回值默认的值为-1 */
  18.  
  19. if(src == NULL || dst == NULL){/* 检查源文件和目的地址是不是空串 */
  20.  
  21. printf(wrong command\n); /* 是空串则报错 */
  22.  
  23. return -;
  24.  
  25. }
  26.  
  27. /* 如果源文件路径的最后一个字符是‘/’,则说明源文件不是一个普通文件,而是一个目录 */
  28.  
  29.   if(src[strlen(src)-]=='/'){
  30.  
  31. printf("source file should be a regular file\n");
  32.  
  33. return -;
  34.  
  35. }
  36.  
  37. /* 为最终的目的文件路径分配内存空间
  38.  
  39. * 文件路径由目标目录和源文件的文件名组成
  40.  
  41. * 所以该缓冲区的大小最大是两个文件路径的长
  42.  
  43. */
  44.  
  45.   if( (dst_file = (char *)malloc(strlen(dst) + strlen(src))) == NULL){
  46.  
  47. perror("fail to malloc");
  48.  
  49. return -; /* 如果内存分配不成功则返回-1 */
  50.  
  51.   }
  52.  
  53.   strcpy(dst_file, dst); /* 将目标目录复制到dst_file缓冲区中 */
  54.  
  55.   /* 如果目标目录的结尾不是‘/’,则添加‘/’,例如/home/admin—>/home/admin/ */
  56.  
  57.   if(dst_file[strlen(dst_file) - ] != '/')
  58.  
  59. strcat(dst_file, "/");
  60.  
  61.   p = rindex(src, '/'); /* 取源文件路径中最后一个‘/’,其后面是文件名 */
  62.  
  63.   strcat(dst_file, p + ); /* 跳过‘/’,得到源文件文件名 */
  64.  
  65. /* 如果目标文件不存在则创建该文件,使用权限字“0644”
  66.  
  67. * 该权限表示所有者拥有读写权限而组用户和其他用户只有读权限
  68.  
  69. * 如果该文件存在,则将文件截短为0打开,覆盖原文件
  70.  
  71. */
  72.  
  73.   if((fd = open(dst_file, O_WRONLY | O_CREAT | O_TRUNC, )) == -){
  74.  
  75. perror("fail to open dst-file");
  76.  
  77. goto end2; /* 打开文件失败,退出时只需要释放dst_file的内存,而不需要关闭 文件 */
  78.  
  79.   }
  80.  
  81.   if(fstat(fd, &statbuf) == -){ /* 取目标文件的文件状态 */
  82.  
  83. perror("fail to stat dst-file");
  84.  
  85. goto end1;
  86.  
  87.   }
  88.  
  89. /* 如果目标文件已经存在,且不是一个普通文件,则无法传输
  90.  
  91. * 因为这样会造成将已经存在的目录等其他特殊文件覆盖
  92.  
  93. */
  94.  
  95.   if(!S_ISREG(statbuf.st_mode)){
  96.  
  97. printf("dst-file should be a regular file");
  98.  
  99. goto end1;
  100.  
  101.   }
  102.  
  103. sprintf(buf, "GET %s", src); /* 准备向服务器端发送GET命令 */
  104.  
  105.   if(my_write(sock_fd, buf, strlen(buf)+) == -) /* 发出命令 */
  106.  
  107. goto end1; /* 出错则退出 */
  108.  
  109.   if( (n = my_read(sock_fd, buf, MAX_LINE)) <= ){ /* 接收服务器端发回的确 认信息 */
  110.  
  111. goto end1;
  112.  
  113.   }
  114.  
  115.   if(buf[] == 'E'){ /* 如果收到的信息是ERR,表示出错 */
  116.  
  117. write(STDOUT_FILENO, buf, n); /* 向屏幕输出服务器发来的出错信息 */
  118.  
  119. res = ;
  120.  
  121. goto end1;
  122.  
  123.   }
  124.  
  125. /* 如果对没有出错,服务器反馈的信息格式为“OK 请求文件的文件大小”
  126.  
  127. * 跳过字符串“OK”和一个空格,从第三个字符开始,取得文件的长度
  128.  
  129. */
  130.  
  131.   len = atoi(&buf[]);
  132.  
  133. /* 向服务器发出准备好的信息,服务器接收到此信息后开始传送文件的内容 */
  134.  
  135.   if(my_write(sock_fd, "RDY", ) == -)
  136.  
  137. goto end1;
  138.  
  139.   while(){ /* 循环读,直到读完所有的文件内容 */
  140.  
  141. n = my_read(sock_fd, buf, MAX_LINE); /* 读服务器传送来的内容 */
  142.  
  143. if(n > ){ /* 读到的字节数大于0,说明正常 */
  144.  
  145. write(fd, buf, n); /* 将读到的内容写到打开的目标文件中去 */
  146.  
  147. len -= n; /* 文件总长度减少 */
  148.  
  149. }else if(len == ){ /* 读到的字节数等于0,说明通信已经结束 */
  150.  
  151. /* 如果请求的文件已经读完,则正常结束此次命令的执行 */
  152.  
  153. printf("OK\n");
  154.  
  155. break;
  156.  
  157. }else /* 读到的字节数小于0,出错 */
  158.  
  159. goto end1;
  160.  
  161. }
  162.  
  163.   res = ; /* 运行到此,则函数正常返回,返回值为0 */
  164.  
  165. end1:
  166.  
  167.   free(dst_file); /* 释放动态分配的内存空间 */
  168.  
  169. end2:
  170.  
  171.   close(fd); /* 关闭文件 */
  172.  
  173. return res;
  174.  
  175. }

Web服务器文件传输程序客户端程序实现的更多相关文章

  1. Java获取Web服务器文件

    Java获取Web服务器文件 如果获取的是服务器上某个目录下的有关文件,就相对比较容易,可以设定死绝对目录,但是如果不能设定死绝对目录,也不确定web服务器的安装目录,可以考虑如下两种方式: 方法一: ...

  2. Linux下用gSOAP开发Web Service服务端和客户端程序

    网上本有一篇流传甚广的C版本的,我参考来实现,发现有不少问题,现在根据自己的开发经验将其修改,使用无误:另外,补充同样功能的C++版本,我想这个应该更有用,因为能用C++,当然好过受限于C. 1.gS ...

  3. Linux下用gSOAP开发Web Service服务端和客户端程序(一)

    1.功能说明: 要开发的Web Service功能非常简单,就是一个add函数,将两个参数相加,返回其和. 2.C版本的程序: (1)头文件:SmsWBS.h,注释部分不可少,url部分的IP必须填写 ...

  4. DELPHI XE5开发WEB服务器及安卓手机客户端

    Xe5开发web服务端和手机客户端 ------------------------------------- Delphi xe5作为最新开发利器,就类似如当年的DELPHI,功能强大,快发速度快, ...

  5. TCP打开文件传输(客户端code)

    #include <stdio.h>#include <stdlib.h>#include <arpa/inet.h>#include <sys/types. ...

  6. java web服务器文件的下载(有下载弹出匡)

    昨天做了一个文件从服务下载的功能,怎么都不弹出页面,下载框.后查询得知.目前两种方法 1.<a href='下载路径' /> 2.window.location.href = basePa ...

  7. Ubuntu django+nginx 搭建python web服务器文件日志

    uwsgi 配置文件 [uwsgi] http-socket = 127.0.0.1:8080 # 项目目录 chdir=/home/ubuntu/mkweb # 指定项目的application m ...

  8. web服务器,应用程序服务器,http服务器的区别

    WEB服务器.应用程序服务器.HTTP服务器有何区别?IIS.Apache.Tomcat.Weblogic.WebSphere都各属于哪种服务器? 这个概念很重要. Web服务器的基本功能就是提供We ...

  9. [源码]Python简易http服务器(内网渗透大文件传输含下载命令)

    Python简易http服务器源码 import SimpleHTTPServerimport SocketServerimport sysPORT = 80if len(sys.argv) != 2 ...

随机推荐

  1. 自定义View总结

    写的很好,代你分析原码,关于 View Measure 测量机制,让我一次把话说完

  2. C语言基础之水仙花数

    题目:打印出所有的“水仙花数”,所谓“水仙花数”是指一个三位数,其各位数字立方和等于该数本身. 例如:153是一个“水仙花数”,因为153=1的三次方+5的三次方+3的三次方. 程序分析:利用for循 ...

  3. iOS 7 SDK: 如何使用后台获取(Background Fetch)

    本文转载至 http://www.cocoachina.com/applenews/devnews/2013/1114/7350.html 本文主要教你如何使用iOS 7 SDK多任务处理API--B ...

  4. sessionStorage存储json对象

    应用场景: 账单列表中A页面:点击其中的一列,ajax返回的数据在这一页 点击进入账单详情B页面: 因为在A页面已经做过ajax的请求了,所以希望把当前其中的一个数组对象传到B页面中,所以,就考虑到暂 ...

  5. HDU 5306 Gorgeous Sequence[线段树区间最值操作]

    Gorgeous Sequence Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Othe ...

  6. iOS 7 Master-Detail模板不好用

    将storyboard->use size classes disabled

  7. js 空正则匹配任意一个位置

    看一个正则 这里明显,起到匹配作用的是 | 后的,可 | 后什么都没有,原理不知道,也没有搜到文献,只有在 Reg101 上是这样解释的, 所以得出结论: js 中,空正则匹配任意一个位置. 不过,这 ...

  8. Zabbix监控Windows主机

    一,下载zabbix-agent 下载地址:http://www.zabbix.com/downloads/3.0.0/zabbix_agents_3.0.0.win.zip 已经下载好的文件 zab ...

  9. SqueezeNet

    虽然网络性能得到了提高,但随之而来的就是效率问题(AlexNet VGG GoogLeNet Resnet DenseNet) 效率问题主要是模型的存储问题和模型进行预测的速度问题. Model Co ...

  10. Drainage Ditches---hdu1532(最大流, 模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1532 最大流模板题: EK:(复杂度为n*m*m); #include<stdio.h> ...