Skip to content

Latest commit

 

History

History
211 lines (182 loc) · 7.01 KB

README.md

File metadata and controls

211 lines (182 loc) · 7.01 KB

交互时序

     客户端                 服务端                
        |                    |
     puts命令---------------->|            
        |         接收命令消息,关闭EPOLL监听
        |                    |
        |<------文件名查重,发送是否传送消息
     接收消息                 |
        |                    |
 发送sha1(或停止)------------>|
        |               接收sha1-----------------或停止
        |                    |                     |
        |<-----------sha1查重,发送是否传秒传--秒传    |
        |                    |               |     |
  sendfile(或秒传)----------->|               |     |
                          接收文件            |     |
                             |               |     |
                           更新表-------------|     |
                             |                     |
                        打开EPOLL监听---------------|

上传功能的实现

  1. 服务端

    void putsCommand(task_t * task, MYSQL* conn) {
        delEpollReadfd(task->epfd, task->peerfd);   
        printf("execute puts command.\n");
        File train;
        memset(&train, 0, sizeof(train));
        // 初始化虚拟文件表
        file_table f1;
        memset(&f1, 0, sizeof(f1));
        f1.parent_id = client_users[task->peerfd].pwd_id;
        f1.owner_id = client_users[task->peerfd].user_id;
        strcpy(f1.file_name, task->data);
        f1.type = 'f';
    
        //文件名查重后,向客户端发送是否可传的消息
    	int ret = select_file_table(conn, &f1);
        if (ret == 0) {
            send(task->peerfd, "off", 3, 0);
            addEpollReadfd(task->epfd, task->peerfd);
            return;
        }
        send(task->peerfd, "on", 2, 0);
    	
        
        // 接收客户端发来的hash数值(此时是sha-1)
        printf("接收hash\n");
        char hash_value[64] = { 0 };
        recvn_server(task->peerfd, &train.length, sizeof(train.length));
        recvn_server(task->peerfd, &train.content, train.length);
        memcpy(hash_value, train.content, train.length); 
        printf("hash: %s\n", hash_value);
        bzero(&train, sizeof(train));
    
        // 在虚拟文件表中查找是否存在sha1
        strcpy(f1.sha1, hash_value); 
        ret = search_file(conn, hash_value);
    
        // 接收第三个小火车的内容
        off_t filesize;
        printf("接收文件长度\n");    
        recvn_server(task->peerfd, &train.length, sizeof(train.length));
        recvn_server(task->peerfd, train.content, train.length);
        memcpy(&filesize, train.content, sizeof(off_t));
        f1.file_size = filesize;
        printf("filesize = %ld\n", filesize);
    
        //文件不存在
        if(ret == -1){
            //发送不秒传消息
            File train = {3, "off"};
            send(task->peerfd, &train, sizeof(train.length)+train.length, MSG_NOSIGNAL);
            bzero(&train, sizeof(train));        
            // // 打开并写入文件
            char temp[4096] = {0};
            sprintf(temp, "./User/%s", hash_value);
            printf("temp = %s\n", temp);
            int fd = open(temp, O_RDWR | O_TRUNC | O_CREAT, 0666);
            // 接收sendfile 发来的逻辑
            ftruncate(fd, filesize);
            printf("ftruncate done, filesize = %ld\n", filesize);
            char* p = (char*)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
            perror("mmap\n");
            printf("接收文件\n");       
            recvn_server(task->peerfd, p, filesize);
            printf("map done\n");
            munmap(p, filesize);
            perror("munmap\n");
            //上传任务执行完毕之后,再加回来
            printf("unmap done\n");
            printf("upload done!\n");
    		// 更新虚拟文件表
            add_file_table(conn, &f1);
        }else{//秒传,并直接更新文件表
            File train = {2, "on"};
            send(task->peerfd, &train, sizeof(train.length)+train.length, MSG_NOSIGNAL);
            add_file_table(conn, &f1);
            printf("speed_transfer_done!\n");
        }
        addEpollReadfd(task->epfd, task->peerfd);
    }

    难点:

    • 接收文件要关掉EPOLL监听
  2. 客户端

    void putsCommand(int sockfd, const char* filename){
        sleep(2);
        char msg[4] = {0};
        // 如果文件名存在,不进行上传
        ssize_t ret = recv(sockfd, msg, sizeof(msg), 0);
        if (ret == 2) {
            upload(sockfd, filename);
        }
        else {
            puts("文件名已存在");
        }
    }
    int upload(int netfd, const char* filename){
        File train;
        bzero(&train, sizeof(train));
        // 打开文件
        char temp[4096] = {0}; 
        sprintf(temp, "./User/%s", filename);
        int fd = open(temp, O_RDONLY);
        if (fd == -1) {
            perror("open file error");
        }
        // 计算 sha1 值
        char hash_value[41]; 
        file_sha1(fd, hash_value); //假设 file_sha1正确地计算并存储了SHA1值
        fd = open(temp, O_RDONLY);
        printf("%s\n", hash_value);
        // 填充小火车
        train.length = strlen(hash_value);
        memcpy(train.content, hash_value, train.length);
        // 发送小火车
        ssize_t totalLen = sizeof(train.length) + train.length;
        ssize_t ret = send(netfd, &train, totalLen, MSG_NOSIGNAL); // 这里检查返回值以确保所有数据都被发送出去
        if (ret == -1)// 如果上传失败
        {
            perror("send error");
        }
        else if (ret < totalLen)//如果没有上传完毕
        {
            printf("Not all data was sent. Only %zu of %zu bytes were sent\n", ret, totalLen);
        }
    
        // 清零操作
        bzero(&train, sizeof(train));
        // 第三次发送文件大小和内容
        struct stat statbuf;
        fstat(fd, &statbuf);
        printf("filesize = %ld\n", statbuf.st_size);
        train.length = sizeof(statbuf.st_size);
        memcpy(train.content, &statbuf.st_size, train.length);
        send(netfd, &train, sizeof(train.length)+train.length, MSG_NOSIGNAL);
    
        bzero(&train, sizeof(train));
        // 接收是否妙传的消息
        recvn_client(netfd, &train.length, sizeof(train.length));
        recvn_client(netfd, &train.content, train.length);
        char* speed_transfer = (char *)malloc(128*sizeof(char));
        strcpy(speed_transfer,train.content);
        printf("speed_transfer: %s\n", speed_transfer);
        bzero(&train, sizeof(train));
        
        int is_speed_transfer = strcmp(speed_transfer, "off");
        if(is_speed_transfer == 0){
            printf("发送文件\n");
            send_file(netfd, fd, statbuf.st_size);
        }else{// 秒传
            printf("file is uploaded!\n");
        }
        
        return 0;
    }

相关SQL语句

  • 文件查重

    const char* sql = "SELECT * FROM user_table WHERE user_name = ?";
  • 哈希查重(sha1)

    const char* cmd = "SELECT file_name, file_size, type FROM file_table WHERE parent_id = ? AND owner_id = ? ";