linux系统编程小项目——仿ftp

项目要求

linux系统编程小项目——仿ftp_第1张图片

日志模块

日志模块需要向文件里面输出信息,所以我们希望能像printf一样参数个数可变,所以声明时带上变量 …,void log_write(const char *format, …);,该函数用法与printf一样——在函数内部需要使用其他东西来接收这个个数可变的参数,所以我们使用了va_list ,(参考: https://blog.csdn.net/qq_19923217/article/details/81385132 ), 该变量可以用来接收变参,步骤有三步

  • 定义变量 va_list args;
  • 创建变量 va_start(args,format); 第二个参数为确定的参数,
  • 销毁变量 va_end(args);

并且使用了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

你可能感兴趣的:(c,linux)