• 私人云盘(自动云同步)


    一、项目简介

            模仿小米的云服务,实现一个通过TCP实现的私人云盘,因为能力有限,所以只实现自动云同步这一个功能,具体可以分为三个小功能,即保持云端和终端数据一致、实现文件的上传与下载以及手动同步

    二、涉及到的知识点

            主要有文件的打开与关闭,文件的发送与接收,文件的写入及socket通信,为了项目整体的间接性,还使用到的枚举类型的结构体以及链表

    三、TCP通信实现流程图

    四、讲解

    在项目实现过程中,代码程序修改了多次,我将最终的项目程序压缩包上传到了我的资源上面,有兴趣的可以自行下载。

    这里放的程序是我在编写项目程序过程中的一个版本,只实现了单个文件的传输,需要自己手动输入要传输的文件,最终的版本在资源里面请不要搞错了

    1、文件说明:

            ①client.c       是客户端的代码

            ②server.c      是服务器端的代码

            ③tcp.c          是客户端和服务器所使用到的一些头文件,以及自己封装的一些函数和自定义的宏

            ④Makefile     这个就不用多说了吧

    2、程序文件

    client.c文件

    #include "tcp.h"
    #define FILENAME "森林风声-呜呼呜呼-树木摇曳.mp3"

    int main(int argc,char *argv[]){
        int socketfd,filefd;
        int ret;
        char buf[BUFSIZ];
        /*检查参数*/
        Argment(argc,argv);
        /*创建套接字并对其初始化*/
        socketfd = SocketInit_Client(argv);
        /*打开文件*/
        filefd = open(FILENAME,O_RDONLY);
        if(filefd == -1){
            ErrExit("open");
        }
        /*发送文件名字*/
        SocketDataHandle(socketfd,FILENAME,strlen(FILENAME),(DataHand_t)send);
        SocketDataHandle(socketfd,buf,1,recv);
        /*发送文件内容*/
        if(buf[0] == OK){
            while(1){
                do{
                    ret = read(filefd,buf,BUFSIZ);
                }while(ret < 0 && errno == EINTR);
            if(ret < 0){
                ErrExit("read");
            }
            if(!ret){
                break;
            }
            ret = SocketDataHandle(socketfd,buf,ret,(DataHand_t)send);
            if(!ret){
                break;
            }
            }
        }
        close(filefd);
        close(socketfd);
        return 0;
    }


    server.c文件

    #include "tcp.h"

    int main(int argc,char *argv[]){
        int socketfd,newsocketfd,filefd;
        int ret;
        char buf[BUFSIZ] = {};
        Addr_in clientaddr;
        socklen_t addrlen = sizeof(Addr_in);
        /*检查参数*/
        Argment(argc,argv);
        /*创建套接字*/
        socketfd = SocketInit_server(argv);
        /*接收客户端的连接并生成一个新的套接字*/
        do{
            newsocketfd = accept(socketfd,(Addr *)&clientaddr,&addrlen);
         }while(newsocketfd < 0 && errno == EINTR);  //erron=EINTR如果信号导致的中断,重新执行一次
        if(newsocketfd == -1){
            ErrExit("accept");
        }
        /*接收文件名字*/
        ret = SocketDataHandle(newsocketfd,buf,BUFSIZ,recv);
        /*创建文件*/
        filefd = open(buf,O_WRONLY|O_CREAT,0660);
        if(filefd == -1){
            ErrExit("open");
        }
        buf[0] = OK;
        SocketDataHandle(newsocketfd,buf,1,(DataHand_t)send);
        /*接收文件*/
        while(1){
            ret = SocketDataHandle(newsocketfd,buf,BUFSIZ,recv);
            if(!ret){
                break;
            }
            write(filefd,buf,ret);
        }
        close(filefd);
        close(newsocketfd);
        close(socketfd);
        return 0;
    }
     

    tcp.h文件

    #ifndef _TCP_H_
    #define _TCP_H_

    /*使用的头文件*/
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    /*自己定义的宏*/
    #define ErrExit(msg) do{perror(msg); exit(EXIT_FAILURE);}while(0)
    #define BACKLOG 5
    #define OK '1'
    typedef struct sockaddr Addr;
    typedef struct sockaddr_in Addr_in;
    typedef ssize_t(* DataHand_t)(int ,void *,size_t,int);

    /*函数声明*/
    void Argment(int argc,char *argv[]);
    int SocketInit_Client(char *argv[]);
    int SocketInit_server(char *argv[]);
    int SocketDataHandle(int fd,void *buf,size_t len,DataHand_t datahandle);

    //参数检查函数
    void Argment(int argc,char *argv[]){
        if(argc < 3){
            fprintf(stdin,"%s\n",argv[0]);
            exit(EXIT_FAILURE);
        }
    }

    //初始化客户端套接字函数
    int SocketInit_Client(char *argv[]){
        int socketfd;
        Addr_in addr;
        /*创建套接字*/
        socketfd = socket(AF_INET,SOCK_STREAM,0);
        if(socketfd == -1){
            ErrExit("socket");
        }
        /*设置通信结构体*/
        bzero(&addr,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(atoi(argv[2]));
        if(inet_aton(argv[1],&addr.sin_addr) == 0){
            fprintf(stderr,"Invalid address\n");
            exit(EXIT_FAILURE);
        }
        /*发起连接请求*/
        if(connect(socketfd,(Addr *)&addr,sizeof(addr)) == -1){
            ErrExit("connect");
        }
        return socketfd;
    }

    //初始化服务器端套接字函数
    int SocketInit_server(char *argv[]){
        int socketfd;
        Addr_in addr;
        /*创建套接字*/
        socketfd = socket(AF_INET,SOCK_STREAM,0);
        if(socketfd == -1){
            ErrExit("socket");
        }
        /*设置地址快速重用*/
        int flag = 1;
        if(setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)) == -1){
            perror("setsockopt");
        }
        /*设置通信结构体*/
        bzero(&addr,sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(atoi(argv[2]));
        if(inet_aton(argv[1],&(addr.sin_addr)) == 0){
            fprintf(stderr,"Invalid address\n");
            exit(EXIT_FAILURE);
        }
        /*绑定通信结构体*/
        if(bind(socketfd,(Addr *)&addr,sizeof(addr)) == -1){
            ErrExit("bind");
        }
        /*设置套接字的模式为监听*/
        if(listen(socketfd,BACKLOG) == -1){
            ErrExit("listen");
        }
        return socketfd;
    }

    //数据处理函数
    int SocketDataHandle(int fd,void *buf,size_t len,DataHand_t datahandle){
        int ret;
        char *str = datahandle == recv?"recv":"send";
        do{
            ret = datahandle(fd,buf,len,0);
        }while(ret < 0 && errno == EINTR);
        if(ret < 0){
            ErrExit(str);
        }
        return ret;
    }

    #endif

    Makefile文件


    all:server client
    CC=gcc
    CFLAGS=-g -Wall

    server:server.c
    client:client.c

    MV_client:
        mv client /mnt/hgfs/Share/

    clean:server client
        rm server client

  • 相关阅读:
    01.下载添加 jar包 Apache Commons Math
    Vue技术9.1天气案例
    【爬虫笔记】Python爬虫简单运用爬取代理IP
    局部指令和全局指令的注册和使用
    【css】sass中的模块化
    android的本地通讯录获取以及RecyclerView展示
    解决vue3 + vite + ts 中require失效的问题(require is not defind)
    【Qt6】列表模型——树形列表
    SpringCloud Config 分布式服务配置
    了解1688API接口测试 | 1688 API接口测试指南
  • 原文地址:https://blog.csdn.net/qq_54967229/article/details/139606504