日志模块需要向文件里面输出信息,所以我们希望能像printf一样参数个数可变,所以声明时带上变量 …,void log_write(const char *format, …);,该函数用法与printf一样——在函数内部需要使用其他东西来接收这个个数可变的参数,所以我们使用了va_list ,(参考: https://blog.csdn.net/qq_19923217/article/details/81385132 ), 该变量可以用来接收变参,步骤有三步
并且使用了vfprintf()函数向文件输出内容。
函数原型:
#include
int vfprintf(FILE *stream, const char *format, va_list ap);
消息模块是服务端与客户端公用的的结构体,每次通信都是收发一整个结构体来通信,结构体包含命令,md5值,还有具体数据。
struct Msg{
// 命令参数
char cmd[32];
// md5值
char md5[64];
//内容
char data[5000];
};
历史信息主要使用在服务器端,每次客户端发送给服务端的指令都会被保存在链表里,在有需要时(客户端发送history时)会该链表的内容被发送到客户端,客户端再输出至屏幕。
struct History{
char cmd[32];
struct History *next;
};
共用函数模块定义了一些客户端和服务端共同使用的函数,
#ifndef __UTILS_H__
#define __UTILS_H__
/**
* 一些公共的函数
*
*/
#include
#include
#include
char* split_str(char *in_cmd); // 以单个空格切割字符串
char* split_str_plus(char *in_cmd); // 升级版,可以切割多个空格之后的指令
char* getMd5(char *filename); // 获取md5 值
long getFilelen(char *filename); // 获取文件长度 使用ftell
#endif
服务端定义了一个服务器,该服务器绑定了IP地址为192.168.64.128(该IP地址为我虚拟机点IP地址,每个人都不一样),端口号为8888。然后等待客户端接入。
struct Msg *handle_cmd(struct Msg *in_cmd,struct Msg *out_cmd){
linklist_insert(&g_head,in_cmd->cmd);
strcpy(out_cmd->cmd,in_cmd->cmd);
if(strncmp(in_cmd->cmd,"ls",2)==0){
FILE *fp;
fp = popen(in_cmd->cmd,"r");
fread(out_cmd->data,sizeof(out_cmd->data),1,fp);
pclose(fp);
return out_cmd;
}else if(strncmp(in_cmd->cmd,"cd",2)==0){
char *p = split_str_plus(in_cmd->cmd);
if(p == NULL){
strcpy(out_cmd->data,"no this path");
return out_cmd;
}else {
if(chdir(p)==-1){
strcpy(out_cmd->data,"cd error");
return out_cmd;
} else{
strcpy(out_cmd->data,"cd success");
return out_cmd;
}
}
}else if(strcmp(in_cmd->cmd,"history\n")==0){
linklist_print(g_head,out_cmd);
return out_cmd;
}else if(strncmp(in_cmd->cmd,"get",3)==0){
FILE *fp;
char *p = split_str_plus(in_cmd->cmd);
long flen = getFilelen(p);
if(flen==-1){
log_write("get error\n");
strcpy(out_cmd->data,"get error");
return out_cmd;
}
log_write("%ld\n",flen);
fp = fopen(p,"r");
if(fp!=NULL){
int readn = fread(out_cmd->data,1,flen,fp);
log_write("read %d\n",readn);
fclose(fp);
char *md5 = getMd5(p);
if(md5!=NULL){
strcpy(out_cmd->md5,md5);
}
return out_cmd;
}else {
log_write("get error\n");
strcpy(out_cmd->data,"get error");
fclose(fp);
return out_cmd;
}
}else if (strncmp(in_cmd->cmd,"put",3)==0){
char *p = split_str_plus(in_cmd->cmd);
FILE *fp = fopen(p,"w");
if(fp!=NULL){
int writen = fwrite(in_cmd->data,1,strlen(in_cmd->data),fp);
log_write("put write %d\n",writen);
fclose(fp);
strcpy(out_cmd->data,"put success");
}else {
strcpy(out_cmd->data,"put error");
fclose(fp);
}
char *md5 = getMd5(p);
if(md5!=NULL){
if(strncmp(in_cmd->md5,md5,32)!=0){
remove(p);
strcpy(out_cmd->data,"md5 diff");
}
}
return out_cmd;
}else if (strncmp(in_cmd->cmd,"quit",4)==0){
log_write("client quit\n");
strcpy(out_cmd->data,"BYEBYE");
return out_cmd;
}else {
strcpy(out_cmd->data,"CMD ERROR");
return out_cmd;
}
}
这个函数是服务器端处理客户端发送过来的指令,客户端将用户输入的指令通过共用信息模块来传输(字符串形式),在这个函数中对指令字符串进行切割解析,对发送的结构体进行填充,并且向客户端进行回复。
程序源码: https://gitee.com/yxxcx/ftppro