#include #include #include #include #include #include #include #include #include #include #include #include #include #include "ftree.h" #include "hash.h" int depth = 0; #define MAXDATASIZE 100 /* max number of bytes we can get at once */ /* * Concatenates child path to parent path. */ void complete_path(const char *parent, const char *child, char *dest) { strcpy(dest, parent); strcat(dest, "/"); strcat(dest, child); } /* * Creates and returns a new socket connection. */ int accept_connnection(char *host, unsigned short port) { int sockfd; struct hostent *he; struct sockaddr_in their_addr; /* connector's address information */ if ((he=gethostbyname(host)) == NULL) { /* get the host info */ perror("gethostbyname"); exit(1); } if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(1); } their_addr.sin_family = AF_INET; /* host byte order */ their_addr.sin_port = htons(port); /* short, network byte order */ their_addr.sin_addr = *((struct in_addr *)he->h_addr); bzero(&(their_addr.sin_zero), 8); /* zero the rest of the struct */ if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) { perror("connect"); exit(1); } return sockfd; } /* * Copies file tree with root based at src to server. * r_src is the root for the tree on the server. */ int rcopy_client(char *src, char *host, unsigned short port, char *r_src, int type) { int sockfd = accept_connnection(host, port); int fcalls = 0; // keep track of children. int final = 0; // return value. // 文件、文件夹的属性 struct stat properties; // 请求结构体,包含路径信息等 struct request r; // lstat 获取文件属性,比如文件权限,文件属主,文件大小等属性。 if (lstat(src, &properties) == -1) { perror("client: lstat"); exit(1); } if (!S_ISLNK(properties.st_mode)) { // skip links. strcpy(r.path, r_src); r.mode = properties.st_mode; r.size = properties.st_size; // 判断是否目录 if (!S_ISDIR(properties.st_mode)) { if (type == 0) { r.type = REGFILE; } else { r.type = TRANSFILE; } FILE *in; // 文件指针 if ((in = fopen(src, "rb")) != NULL) { hash(r.hash, in); fclose(in); } else { perror("client: fopen"); exit(1); } } else { r.type = REGDIR; } int t, m; t = htonl(r.type); m = htonl(r.mode); if (write(sockfd, &t, sizeof(int)) == -1) { perror("client: writing type"); exit(1); } if (write(sockfd, &r.path, MAXPATH) == -1) { perror("client: writing path"); exit(1); } if (write(sockfd, &m, sizeof(int)) == -1) { perror("client: writing mode"); exit(1); } if (write(sockfd, &r.size, sizeof(int)) == -1) { perror("client: writing size"); exit(1); } if (write(sockfd, &r.hash, BLOCKSIZE) == -1) { perror("client: writing hash"); exit(1); } // 发送文件 if (r.type == TRANSFILE) { FILE *out; if ((out = fopen(src, "rb")) != NULL) { int s = 0; char byte; while ((fread(&byte, 1, 1, out) == 1) && s++ < r.size) { write(sockfd, &byte, 1); } fclose(out); } else { perror("client: fopen"); exit(1); } } // 读取socket数据 int status, len; len = read(sockfd, &status, sizeof(int)); if (len != sizeof(int)) { perror("client: reading from socket"); exit(1); } else { if (status == SENDFILE) { // 获取pid pid_t pid = fork(); if (pid == 0) { close(sockfd); // child will initate a new connection. depth++; rcopy_client(src, host, port, r_src, TRANSFILE); } else { fcalls++; } } if (status == OK) { // 如果是目录 if (S_ISDIR(properties.st_mode)) { DIR *curr; if ((curr = opendir(src)) == NULL) { perror("client: opendir"); exit(1); } struct dirent *child; // 递归调用目录 while ((child = readdir(curr)) != NULL) { if (strncmp(child->d_name, ".", 1) != 0) { pid_t pid = fork(); if (pid == 0) { depth++; char client_child_path[MAXPATH]; char server_child_path[MAXPATH]; complete_path(src, child->d_name, client_child_path); complete_path(r_src, child->d_name, server_child_path); close(sockfd); rcopy_client(client_child_path, host, port, server_child_path, 0); } else { fcalls++; } } } } // 文件的执行结果 printf("File:%s,size:%dkb has been send successfully.\n",r.path,r.size); } if (status == ERROR) { fprintf(stderr, "client: error occured with %s\n", src); final = 1; } } } close(sockfd); int status; char pid; while (fcalls > 0) { wait(&status); if (WIFEXITED(status)) { // 从子进程的终止状态中提取出退出状态码 pid = WEXITSTATUS(status); if (!final) { final = pid; } } else { perror("client: exit"); exit(1); } --fcalls; } if (depth == 0) { return final; } else { exit(final); } }