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) == -) /* 将命令拆分为命令名和命令参数的形式*/

               exit();

        /* 根据命令名作命令处理,比较命令名和每一个合法命令,匹配则处理 */

        if(strcasecmp(command.name, "get") == ){ /* get命令 */

               if(do_get(command.argv[], command.argv[], sock_fd) == -)

                      exit(); /* 命令处理出错则退出 */

        }else if(strcasecmp(command.name, "put") == ){ /* put命令 */

               if(do_put(command.argv[], command.argv[], sock_fd) == -)

                      exit();

        }else if(strcasecmp(command.name, "cd") == ){ /* cd命令 */

               if(do_cd(command.argv[]) == -)

                      exit();

        }else if(strcasecmp(command.name, "!cd") == ){ /* !cd命令 */

               if(do_serv_cd(command.argv[], sock_fd) == -)

                      exit();

        }else if(strcasecmp(command.name, "ls") == ){ /* ls命令 */

               if(do_ls(command.argv[]) == -)

                      exit();

        }else if(strcasecmp(command.name, "!ls") == ){ /* !ls命令 */

               if(do_serv_ls(command.argv[], sock_fd) == -)

                      exit();

        }else if(strcasecmp(command.name, "connect") == ){ /* connect               命令 */

               if(do_connect(command.argv[], &sin, &sock_fd) == -)

                      exit();

        }else if(strcasecmp(command.name, "bye") == ){ /* bye命令 */

               if(do_bye(sock_fd) == -)

                      exit();

               break; /* 向服务器端发送退出信息后,则跳出循环,退出程序 */

        }else{ /* 错误命令,打印程序的用法 */

               printf("wrong command\n");

               printf("usage : command arg1, arg2, ... argn\n");

        }

        printf("myftp$: "); /* 再次打印提示符,准备接受新的命令 */

        fflush(stdout);

}

if(close(sock_fd) == -){ /* 执行bye命令后执行到这里,关闭通信用套接字 */

        perror("fail to close");

        exit();

}

return ; /* 程序正常退出 */

}

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

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

程序代码如下:

#include "common.h"

/*下面的宏将cline串中从pos所表示的位置开始,跳过连续的空格和制表符 */

#define del_blank(pos, cline); { \

while(cline[pos] != '\0' && (cline[pos] == ' ' || cline[pos] == '\t'))\

{ \

        pos++; \

} \

}

/* 下面的宏得到一个命令参数。

* 将cline串中从pos所表示的位置的内容复制到arg缓冲区中,直到遇到空格、制表符或者结束符为止

*/

#define get_arg(arg, pos, cline); { \

int i = ; \

while(cline[pos] != '\0' && 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 = ;

clien[strlen(cline) - ] = '\0'; /* 得到命令字符串的长度,将最后一个‘\n’                                                               替换为‘\0’ */

del_blank(pos, cline);                    /* 过滤空格,直到遇到第一个参数 */

i = ;

while(cline[pos] != '\0'){ /* 处理整个命令字符串 */

        /* 为存储命令参数的数组分配空间 */

        if((command->argv[i] = (char *)malloc(MAX_LENGTH)) == NULL){

               perror("fail to malloc");

               return -;

        }

        /* 得到一个参数,将两个空格之间的内容复制到存储参数的数组 */

        get_arg(command->argv[i], pos, cline);

        i++;                                       /* 下一个参数 */

        del_blank(pos, cline);     /* 过滤掉空格 */

}

command->argv[i] = NULL;          /* 命令参数数组以NULL结尾 */

comand->name = command->.argv[]; /* 命令名和第一个命令参数实际上指向同一块内存区域 */

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) == -){

        perror("wrong format of ip address"); /* 如果地址格式为非法则出错 */

        return -;

}

sin.sin_port = htons(PORT); /* 将端口号转换成为网络字节序存储在地址结构中 */

if(sfd = socket(AF_INET, SOCK_STREAM, )) == -){ /* 创建套接字 */

        perror("fail to creat socket");

        return -;      

}

/* 使用该套接字,和填充好的地址结构进行连接 */

if(connect(sfd, (struct sockaddr *)sin, sizeof(struct sockaddr_in)) ==     -){

        perror("fail to connect");

        return -;

}

*sock_fd = sfd; /* 将sfd保存的套接字的描述符赋值给sock_fd所指向区域返回 */

return ;

}

(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 */

if(src == NULL || dst == NULL){/* 检查源文件和目的地址是不是空串 */

        printf(wrong command\n);  /* 是空串则报错 */

        return -;

}

/* 如果源文件路径的最后一个字符是‘/’,则说明源文件不是一个普通文件,而是一个目录   */

  if(src[strlen(src)-]=='/'){

        printf("source file should be a regular file\n");

        return -;

}

/* 为最终的目的文件路径分配内存空间

* 文件路径由目标目录和源文件的文件名组成

* 所以该缓冲区的大小最大是两个文件路径的长

*/

  if( (dst_file = (char *)malloc(strlen(dst) + strlen(src))) == NULL){

        perror("fail to malloc");

        return -;                       /* 如果内存分配不成功则返回-1 */

  }

  strcpy(dst_file, dst);         /* 将目标目录复制到dst_file缓冲区中 */

  /* 如果目标目录的结尾不是‘/’,则添加‘/’,例如/home/admin—>/home/admin/ */

  if(dst_file[strlen(dst_file) - ] != '/')

        strcat(dst_file, "/");

  p = rindex(src, '/');              /* 取源文件路径中最后一个‘/’,其后面是文件名 */

  strcat(dst_file, p + );    /* 跳过‘/’,得到源文件文件名 */

/* 如果目标文件不存在则创建该文件,使用权限字“0644”

* 该权限表示所有者拥有读写权限而组用户和其他用户只有读权限

* 如果该文件存在,则将文件截短为0打开,覆盖原文件

*/

  if((fd = open(dst_file, O_WRONLY | O_CREAT | O_TRUNC, )) == -){

        perror("fail to open dst-file");

        goto end2; /* 打开文件失败,退出时只需要释放dst_file的内存,而不需要关闭            文件 */

  }

  if(fstat(fd, &statbuf) == -){ /* 取目标文件的文件状态 */

        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)+) == -) /* 发出命令 */

        goto end1;                                                               /* 出错则退出 */

  if( (n = my_read(sock_fd, buf, MAX_LINE)) <= ){ /* 接收服务器端发回的确                                                                                       认信息 */

        goto end1;

  }

  if(buf[] == 'E'){ /* 如果收到的信息是ERR,表示出错 */

        write(STDOUT_FILENO, buf, n); /* 向屏幕输出服务器发来的出错信息 */

        res = ;

        goto end1;

  }

/* 如果对没有出错,服务器反馈的信息格式为“OK 请求文件的文件大小”

* 跳过字符串“OK”和一个空格,从第三个字符开始,取得文件的长度

*/

  len = atoi(&buf[]);

/* 向服务器发出准备好的信息,服务器接收到此信息后开始传送文件的内容 */

  if(my_write(sock_fd, "RDY", ) == -)

        goto end1;

  while(){ /* 循环读,直到读完所有的文件内容 */

        n = my_read(sock_fd, buf, MAX_LINE); /* 读服务器传送来的内容 */

        if(n > ){ /* 读到的字节数大于0,说明正常 */

               write(fd, buf, n); /* 将读到的内容写到打开的目标文件中去 */

               len -= n; /* 文件总长度减少 */

        }else if(len == ){ /* 读到的字节数等于0,说明通信已经结束 */

               /* 如果请求的文件已经读完,则正常结束此次命令的执行 */

               printf("OK\n");

               break;

        }else /* 读到的字节数小于0,出错 */

               goto end1;

}

  res = ; /* 运行到此,则函数正常返回,返回值为0 */

end1:

  free(dst_file); /* 释放动态分配的内存空间 */

end2:

  close(fd); /* 关闭文件 */

return res;

}

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. AssetsManager 在ios更新失败解决方案

    AssetsManager在安卓平台使用正常,但是到ios就不行了,最后发现是 cocos2d\cocos\network\CCDownloader-apple.mm中的 - (void)URLSes ...

  2. Mybatis 3.1中 Mapper XML 文件 的学习详解(转载)

    MyBatis 真正的力量是在映射语句中.这里是奇迹发生的地方.对于所有的力量,SQL 映射的 XML 文件是相当的简单.当然如果你将它们和对等功能的 JDBC 代码来比较,你会发现映射文件节省了大约 ...

  3. LeetCode-Water and Jug Problem

    You are given two jugs with capacities x and y litres. There is an infinite amount of water supply a ...

  4. js实现输入框联想搜索

    实现点击和输入搜索联想,把搜索出的列表放到下面的ul列表中,然后再列表中选择并把公司名赋值给输入框,把guid赋值给隐藏域 html <input type="hidden" ...

  5. Angular2+学习第3篇 基本知识-组件

    一.插值表达式 基本用法与ng1一样. 可以使用 Angular 内置的 json 管道,来显示对象信息,管道用来格式化数据 import { Component } from '@angular/c ...

  6. rabbitmq 用户管理

    rabbitmqctl add_user root cor2016 rabbitmqctl set_user_tags root administrator http://host:15672/#/u ...

  7. 160519、Oracle中将查询出的多条记录的某个字段拼接成一个字符串的方法

    with temp as( select 'China' nation ,'Guangzhou' city from dual union all select 'China' nation ,'Sh ...

  8. 微信公众号获取用户openId How to use cURL to get jSON data and decode the data?

    w http://stackoverflow.com/questions/16700960/how-to-use-curl-to-get-json-data-and-decode-the-data

  9. Spark Streaming源码分析 – JobScheduler

    先给出一个job从被generate到被执行的整个过程在JobGenerator中,需要定时的发起GenerateJobs事件,而每个job其实就是针对DStream中的一个RDD,发起一个Spark ...

  10. linux系统分析命令总结

    一张图片