您的位置:首页 > 编程语言 > PHP开发

FTP项目

2019-08-18 23:43 1201 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/weixin_43664986/article/details/99708939

FTP(File Transfer Protocol,文件传输协议) 是 TCP/IP 协议组中的协议之一。FTP协议包括两个组成部分,其一为FTP服务器,其二为FTP客户端。其中FTP服务器用来存储文件,用户可以使用FTP客户端通过FTP协议访问位于FTP服务器上的资源。在开发网站的时候,通常利用FTP协议把网页或程序传到Web服务器上。此外,由于FTP传输效率非常高,在网络上传输大的文件时,一般也采用该协议。

ftp项目通过网络传送文件基本实现的功能:

第一步:
1.设置链表模块
插入 获取所有历史命令 遍历 单元测试

问题:linklist_insert()函数中的二级指针的初始化

2.日志模块用于测试调整
void log_create(const char *filename);

储存信息文件创建
用到fopen函数
FILE *fopen(const char *path, const char *mode);
mode:用a+ 权限,以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)

void log_destroy();
文件的关闭

void log_write(char *format,…);
测试消息的写入
代码:
void log_write(char *format,…)//写入
19 {
20 va_list args;//定义一个变量
21
22 va_start(args,format);//创建
23 vfprintf(g_log,format,args);
//int vfprintf(FILE *stream, const char *format, va_list ap);

24
25 va_end(args);//销毁
26
27 fflush(g_log);//刷新缓存,强制写入文件
28 …
29 }

注意:
文件创建后一定要有关闭文件,不然会导致文件错误。
文件写入函数中一定要有fflushu函数,fflush()会强迫将缓冲区内的数据写回参数stream 指定的文件中. 如果参数stream 为NULL,fflush()会将所有打开的文件数据更新。没有容易导致消息无法写入。同时,如果在写完文件后调用函数fclose关闭该文件,同样可以达到将缓冲区的内容写到文件中的目的,但是那样系统开销较大。

3.公用模块msg。结构体
代码:
#ifndef MSG_H
2 #define MSG_H
3 #define SERVER_PORT 8888//宏定义端口号
4 enum FTP_CMD{//枚举
5 FTP_CMD_LS=0,//LS
6 FTP_CMD_GET=1,//get 下载
7 FTP_CMD_PUT=2,//put 上传
8 FTP_CMD_QUIT=3,//quit 断开连接
9 FTP_CMD_CD=4,//cd 切换服务端目录
10 FTP_CMD_AUTH,//授权
11 FTP_CMD_HIST,//历史记录
12 FTP_CMD_ERROR,//无效的命令
13 };
14
15 struct Msg{
16 enum FTP_CMD cmd;//命令
17 char args[32];//命令行
18 char md5[64];//md5校验值
19 int length_data;//计算文件长度,防止2.txt后面的不是数据部分乱码
20 char data[5000];//data
21 };
22
23 struct Auth{//认证结构体
24 enum FTP_CMD cmd;
25 char username[32];
26 char password[32];
27
28
29 };
30 #endif

注意:枚举函数参考https://blog.csdn.net/weixin_43664986/article/details/99710056
ftp服务器和客户端的构架
参考网络编程:
https://blog.csdn.net/weixin_43664986/article/details/99222217

注意:可搭建不同的两种网络通信,用Linux本机的ip地址"127.0.0.1"或者ifconfig命令知道虚拟机连接的网络地址,inet addr:192.168.175.135进行搭建

第二基本功能实现:
1.ls 显示服务端的文件
客户端接收命令发送给服务端,服务端接收执行,返回命令,客户端打印出ls显示服务端文件
2.get 下载服务端的文件
1.计算文件实际长度
2.分割参数
多个空格
\n
结尾的’\0’
strstr或strtok
3.服务端
打开文件 //读的方式打开(“r”)
读取文件,//保存在msg->data
关闭文件
4.客户端
打开文件 //写的方式打开(“w”)
写入文件,msg->data
关闭文件

问题:在客户端中执行cd a切换目录时,用get可以将文件下载到服务器所在的文件中,但是用put命令却无法间文件上传到服务器所在的文件中。//未解决

ps:
分割参数代码:
int split_string(char *in_str, char *out_str) {
char *first;
char *_n;
//从in_str中查找第一个空格
first = strstr(in_str, " ");
_n = strstr(in_str, “\n”);
//找到\n
while (1) {
//没有找到空格
if (NULL == first || *first == ‘\0’) {
return -1;
}
//移动到下一个空格
first += 1;
//判断当前字符是否空格
if (*first != ’ ‘) {
//找到
break;
}
}
// client.c\n,不拷贝\n
strncpy(out_str, first, _n - first);
//设置文件结尾’\0’
out_str[_n - first] = ‘\0’;
return 0;
}

注意:strstr函数,char *strstr(char *s1, char *s2);
作用:在s1中查找s2子串
返回s2在s1中第一次出现的地址,如果没找到则返回NULL。
https://blog.csdn.net/weixin_43664986/article/details/99589792

3.put 上传本地文件到服务端
客户端
打开文件 //写的方式打开(“w”)
写入文件,msg->data
关闭文件
服务端
打开文件 //读的方式打开(“r”)
读取文件,//保存在msg->data
关闭文件

4.quit退出
用户输入quit
客户端处理quit命令
服务端处理quit命令

执行过程:收到用户写入的quit命令,客户端将命令发给服务端,服务端接收命令后,退出循环,将退出命令发给客户端。

5.cd 切换目录
用户输入cd dir
服务端处理dir
chdir
int chdir(const char *path);
https://blog.csdn.net/weixin_43664986/article/details/99710056

第三 高级功能实现

1.用户名密码验证
在客户端连上后,就发送用户名密码给服务端。等待接收服务端返回信息,成功进入,失败结束程序。
服务端对比用户密码是否一样:不一样的话,服务端断开连接
建立一个passage文件进行校验客户端发来的密码 fscanf(fp1,"%s""%s",serve.username,serve.password);
将读到的用户名和密码存入serve.username,serve.password,然后和客户端发来的消息进行比较.
参考:http://c.biancheng.net/view/2073.html
2.get,put传输的文件,进行md5校验
在get中在服务端读取命令之前获得md5存入msg->md5中
//获取文件的md5
get_md5(filename, msg->md5);
在put中在客户端读取命令之前获得md5存入msg->md5中

get_md5代码:
void get_md5(char *filename,char *md5sum)
58 {
59 //转换成命令md5sum 1.txt
60 char cmd[64];
61 sprintf(cmd,“md5sum %s”,filename);//将md5sum 文件名 给cmd
62 //popen调用md5sun获取文件的md5
63 char md5[64];
64 FILE *fp=popen(cmd,“r”);
65 if(fp!=NULL){
66 int ret=fread(md5,1,sizeof(md5),fp);
67 log_write(“fread ret =%d,md5=%s”,ret,md5);
68 pclose(fp);
69 }
70 //分割md5的值 03dd3973c201444e476e7008407fd23c 1.txt
71 sscanf(md5,"%s",md5sum);//把md5的值给md5sum, 遇到空格就停止
72
73 }

3.hist,显示历史记录,链表
服务端要记录每一次命令
//保存命令
linklist_insert(&hist,in_cmd->args);
识别并发送hist
服务端获取所有命令,并发送
linklist_get_cmd(hist,out_cmd->data);
客户端显示结果

函数代码:
void linklist_insert(struct Linklist **head,char *cmd)
{
//申请新节点
struct Linklist *node=(struct Linklist *)malloc(sizeof(struct Linklist));
node->next=NULL;
strcpy(node->cmd,cmd);
//新节点指向头节点
node->next=*head;
//头节点指针指向新节点
*head=node;

}

void linklist_get_cmd(struct Linklist *head,char *out_buf)
{
struct Linklist *p=head;
char buf[32];
while(p!=NULL){
//在命令后\n
sprintf(buf,"%s\n",p->cmd);
//拷贝命令
strcat(out_buf,buf);//将所有buf给out_buf
//指向下个一个节点
p=p->next;
}
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: