目录

  • 1.题目
  • 2.运行截图
  • 3.总体设计
  • 4.详细设计
  • 5.源码
    • 5.1服务端
    • 5.2客户端

1.题目

1)模仿百度网盘实现一个文件上传、下载、浏览的终端网盘;
2)能够实现文件和目录的存储;
3)在终端下运行服务器和客户端,不对GUI做要求;
4)以用户的形式组织用户数据,不同用户“登录”可以访问相应的数据;
5)同一用户可以在不同位置进行登录,并且需要考虑访问冲突的问题:即用户在写一个文件的时候,该文件不能被伤处;
6)浏览方法的显示参考下面的方式进行显示,同时需要根据参数选择显示文件的属性;
7)写一个测试程序模拟在多进程环境下对服务器程序进行性能测试,需要的数据至少包括多少用户的情况下得到的传输率分别为多少;
8)服务端程序应以Daemon的方式予以实现。

2.运行截图

1)左上方为服务端程序,可以看到此时两个成功连接并已成功退出(执行结束之后才截的图)。其他两个为客户端。注册两个新用户,左下角的客户端上传了普通文件4.c以及目录文件test,然后演示下载目录文件test;右边的客户端上传了目录文件test以及普通文件3.c和4.c,演示了下载普通文件4.c。

2)左边的客户端输入浏览指令-a浏览该用户文件夹12345的全部文件,也就是之前上传的目录文件test以及普通文件4.c,输入具体的文件名称test浏览该文件的所有文件属性信息,右边的客户端也如此。

3)浏览指令处输入-m显示菜单,并输入4退出程序。

4)可以看到服务端以用户账号为文件夹名称新建的两个分别存放各自上传文件的文件夹。

5)文件夹12345有用户12345之前上传的目录文件test以及普通文件4.c。

6)打开test文件夹可以看到里面的两个普通文件。

7)文件夹23456同样存在用户23456上传的目录文件test以及普通文件3.c、4.c。

8)打开客户端存放的下载信息的文件加可以看到用户12345和23456下载的两个文件。

9)打开下载的test文件夹可以看到里面的两个文件。

10)这是服务端存放用户信息的文件,2代表用户总数,12345和12345分别是用户的账号和密码,下面的两行同样如此。

3.总体设计

服务端程序以守护进程的方式运行,传输层通信协议用的是面向连接、可靠的、基于字节流的TCP协议,服务端在主函数创建并绑定套接字之后,处于listen状态,将accept函数放在while无线循环里面,每有一个客户端connect则accept并输出显示连接的客户端的IP地址和PORT端口号,并利用pthread_create函数为该用户创建一个线程,进而实现多用户多线程的访问方式。
之后都是根据客户端的指令执行操作了,客户端和服务端是同步进行的,首先会从文件读入所有用户的账号和密码供验证,没有账户则自动创建,然后可以根据菜单选择上传文件、下载文件、浏览文件还是退出程序,可以对普通文件还是目录文件进行操作。服务端存放的用户文件放在由唯一的该用户的账号创建的文件夹里,用户也只可以对自己的文件夹里面的文件进行操作。由于考虑到C/S模式的性质,对下载的文件就没有必要根据用户账号进行存储了,都是直接存储。每执行一个操作结束之后均会再次显示该菜单,通过函数的形参传递用户的连接套接字、账号以及密码,客户端退出后服务端同样显示退出的客户端IP和PORT。

4.详细设计

这里首先会分别说明服务端和客户端特有的部分,接下来两者的通信公共部分会结合说明。
服务端:首先利用define定义网盘最大用户数MAX_USER以及MAX_ONLINE_CLIEN服务器最大同时在线客户端数、SERVER_PORT服务端口 、BUFFER_SIZE发送接收缓冲区大小。由于上传文件函数Upload、下载文件函数Download和浏览文件函数Browse是互相调用的,所以需要提前声明它们。接下来还有网盘的用户数目current_user、存储存储连接服务端的用户信息结构体User(包括用户账号和密码),两者都是要存入文件net_disk_userinfo.txt文件当中。

然后是服务端的写入数据到文件函数Write_to_file,利用fopen以w写的方式打开该文件,设置完全缓存模式之后利用fprintf函数分别将网盘用户数、用户信息结构体的信息写入文件,最后再利用fclose函数关闭文件。从文件读出数据函数Read_from_file基本同写入的一样,只不过是将fprintf改成fscanf,这里就不再赘述。

到服务端主函数部分利用库函数daemon(int nochdir, int noclose )函数创建守护进程,将两个参数都置为非0,表示保持当前执行目录不变以及表示不重定向标准输入、输出、错误到/dev/null(也就是不输出),保持不变。然后就是利用socket创建套接字,设置相关参数然后利用bind绑定,接着是调用listen使其处于监听状态,最大连接客户机数位MAX_ONLINE_CLIENT。接下来是while循环语句,客户机连接时候将accept然后输出连接的客户机的IP地址和端口号,然后利用pthread_create函数为其创建线程,跳转至用户起始执行函数Client_start_func并传入accept函数返回的客户套接字描述符cfd。

客户端:首先还是利用define定义服务器端口号SERVER_PORT、服务器IP地址SEERVER_IP、发送接收信息缓冲区大小,还有声明上传、下载、浏览三个函数。主函数部分利用socket创建套接字之后就设置相关参数然后connect连接服务端,成功后调用用户起始执行函数。

通信公共部分:用户首先输入五位的数账号然后发送给服务端,服务端创建线程后先从文件当中读入用户数据,然后接收用户账号。接着利用for循环验证是否存在该用户,如果存在也就是strcmp(Usr[i].id, buffer) == 0则等待接收用户发来的密码并验证,用户输入密码后发送给服务端,服务端验证该用户账号对应的密码正确后会发送true给客户,否则发送false然后客户端会利用goto语句返回重新输入密码。若服务端验证该用户不存在则自动为该用户注册账号,将网盘用户数current++,并且接收到的新用户账号密码写入文件,同时服务端调用mkdir且根据用户账号创建存储该用户的文件夹,用于存储用户上传的数据。然后客户端开始输入指令并发送指令给服务端,所输入错误指令则goto返回重新输入。输入1进入上传文件,同时将客户端套接字cfd,账号id以及密码password通过形参传入上传文件函数。客户端首先输入要上传的文件名,然后利用struct stat s_buf和stat(file_name, &s_buf)获取文件信息,并放到s_buf中。if(S_ISDIR(s_buf.st_mode))则上传的文件为目录,则将”dir”发送给服务端,由于send和recv不是一一对应的,所以若是没有输出参数的情况下连续出现send或者是recv,需要在它们的中间分别接收和发送一条无价值的信息,否则会导致接收或发送信息的重合,导致程序出错。

服务端接收到上传的文件是目录文件之后,会首先利用mkdir在该用户的文件夹下面以该目录文件名为文件名创建一个新文件夹。在服务端创建该文件夹的完整路径由客户端发送给服务端,(这里客户端应道basename函数,用于获取文件路径最后的目录文件名或者普通文件名字,不验证文件存在与否,用在客户端上传的文件不在当前目录,需要输入完整的路径名),然后客户端会利创建目录句柄,while((file = readdir(dir)) != NULL) {if((strcmp(file->d_name,"…") != 0) && (strcmp(file->d_name,".") != 0))total_file++;}统计上传的文件数目。然后利用sprintf(buffer, “%d”, total_file);将整型的文件数目变量转化为字符串发送给服务端,服务端接收到后会利用atoi函数再转回整型。

然后客户端接收服务端发送过来的无价值信息,统计完文件数目之后关闭目录重新打开目录使其再次从头读取。客户端从头读取除了“.”和”…”的文件,每次读取将读取到的文件名称连同服务端存储该文件的路径发送给服务端,服务端以写的打开方式打开文件,同时客户端以读的方式打开该文件。客户端读取并发送文件

do{r_size = fread(buffer, sizeof(char), BUFFER_SIZE, fp);send(sfd, buffer, r_size, 0);}while(r_size == BUFFER_SIZE);fclose(fp);

服务端接收信息并将信息写入文件

do{r_size = recv(cfd, buffer, BUFFER_SIZE, 0);fwrite(buffer, sizeof(char), r_size, fp);}while(r_size == BUFFER_SIZE);fclose(fp);

读完写完文件之后关闭文件,然后客户端接收服务端发送过来的无价值信息,接着进入下一次循环开始发送文件,发送接收文件结束之后会再输出输出操作菜单。如果发送的是文件则客户端直接进入将服务端存放该文件的路径以及文件名发到给服务端,接着如同上面发送目录文件一样发送该文件即可,发送结束后还是进入操作菜单。
输入2进入下载文件,基本思路同上传文件一样,只不过是将服务端和客户端的角色交换过来罢了,这里就觉得没必要进行讲解,要不然就显得冗余了。

输入3进入浏览文件,进入while(1)循环,客户端输入指令,输入-m则跳出循环,输出菜单;若输入-a则从服务端读该用户的目录获取该目录里面的所有文件名整合成字符串发送给客户端,客户端再输出;如果客户端输入具体的文件名或者目录名称,则服务端则根据struct stat输出该文件的的所有属性信息,同样在服务端将它们整合成字符串再发送给客户输出显示,会将文件属性的三个时间属性信息以strftime(abuffer, 64, “%Y-%m-%d %H:%M:%S \n”, accesstime)这种方式输出。

5.源码

5.1服务端

/*高级网络程序设计课程设计1.模仿百度网盘实现一个文件上传、下载、浏览的终端网盘;2.能够实现文件和目录的存储;3.在终端下运行服务器和客户端,不对GUI做要求;4.以用户的形式组织用户数据,不同用户“登录”可以访问相应的数据;5.同一用户可以在不同位置进行登录,并且需要考虑访问冲突的问题:即用户在写一个文件的时候,该文件不能被伤处;6.浏览方法的显示参考下面的方式进行显示,同时需要根据参数选择显示文件的属性;7.写一个测试程序模拟在多进程环境下对服务器程序进行性能测试,需要的数据至少包括多少用户的情况下得到的传输率分别为多少;8.服务端程序应以Daemon的方式予以实现;9.源代码和报告提交时间:2020年7月20日。此程序为服务端*/#include <unistd.h>    //read write close
#include <libgen.h>    //basename
#include <stdio.h>     //printf
#include <stdlib.h>    //exit
#include <string.h>    //bzero
#include <pthread.h>   //pthread_crate
#include <sys/socket.h>//socket
#include <arpa/inet.h> //socket
#include <netinet/in.h>//socket
#include <sys/stat.h>  //mkdir
#include <dirent.h>    //readdir/opendir/closedir
#include <time.h>      //strftime
#include <pthread.h>   //pthread#define MAX_USER 10         //网盘最大用户数
#define MAX_ONLINE_CLIENT 20//服务器最大同时在线客户端数
#define SERVER_PORT 8080    //服务端口
#define BUFFER_SIZE 1024    //发送接收缓冲区大小void Upload(int cfd, char id[6], char password[6]);
void Download(int cfd, char id[6], char password[6]);
void Browse(int cfd, char id[6], char password[6]);int current_user = 0;//网盘用户数struct User          //用户信息结构体
{char id[6];      //账号char password[6];//密码
}User[MAX_USER];//写入数据到文件
void Write_to_file()
{FILE *fp;int i = 0;if((fp = fopen("net_disk_userinfo.txt", "w")) == NULL)//以写的方式打开文件perror("file open failed");setvbuf(fp, NULL, _IOFBF, BUFSIZ);                    //设置完全缓存模式fprintf(fp, "%d\n", current_user);while(i < current_user){fprintf(fp, "%s\n%s\n\n", User[i].id, User[i].password);i++;}fclose(fp);
}//从文件读出数据
void Read_from_file()
{FILE *fp;int i = 0;if((fp = fopen("net_disk_userinfo.txt", "r")) == NULL)//以读的方式打开文件perror("file open failed");setvbuf(fp, NULL, _IOFBF, BUFSIZ);                    //设置完全缓存模式fscanf(fp, "%d\n", &current_user);while(i < current_user){fscanf(fp, "%s\n%s\n\n", User[i].id, User[i].password);i++;}fclose(fp);
}//用户上传
void Upload(int cfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE];int r_len = recv(cfd, buffer, BUFFER_SIZE, 0);//接收普通文件还是目录buffer[r_len] = '\0';send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合if(strcmp(buffer, "dir") == 0)//上传目录{int total_file, i;r_len = recv(cfd, buffer, BUFFER_SIZE, 0);//存放该目录文件的完整路径buffer[r_len] = '\0';mkdir(buffer, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);//为目录创建文件夹send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合recv(cfd, buffer, BUFFER_SIZE, 0);//接收的文件数目total_file = atoi(buffer);send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合for(i = 0; i < total_file; i++){bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//存放该文件的完整路径以及文件名send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合FILE *fp = fopen(buffer, "w");//打开文件,准备写入if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = recv(cfd, buffer, BUFFER_SIZE, 0);fwrite(buffer, sizeof(char), r_size, fp);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合}}else//上传普通文件{char file_name[BUFFER_SIZE];bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//接收文件存放完整路径strcpy(file_name, buffer);send(cfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合FILE *fp = fopen(file_name, "w");//打开文件,准备写入if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = recv(cfd, buffer, BUFFER_SIZE, 0);fwrite(buffer, sizeof(char), r_size, fp);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL; }}bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//接收指令if(strcmp(buffer, "1") == 0)Upload(cfd, id, password);else if(strcmp(buffer, "2") == 0)Download(cfd, id, password);else if(strcmp(buffer, "3") == 0)Browse(cfd, id, password);else{struct sockaddr_in addr;//获取下线的客户机的IP和PORTint len = sizeof(addr);getpeername(cfd, (struct sockaddr*)&addr, &len);printf("客户机成功下线 IP: %s   PORT: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));close(cfd);}
}//用户下载
void Download(int cfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE], file_name[BUFFER_SIZE], file_name1[BUFFER_SIZE];recv(cfd, buffer, BUFFER_SIZE, 0);//接收下载的文件的完整路径strcpy(file_name, buffer);struct stat s_buf;//用于判断该文件是目录还是普通文件stat(buffer, &s_buf);//获取文件信息,并放到s_buf中if(S_ISDIR(s_buf.st_mode))//下载目录{send(cfd, "dir", 3, 0);//告诉客户端这是目录文件recv(cfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合int total_file = 0;//目录里面的文件数DIR *dir;struct dirent *file;if(!(dir = opendir(file_name))){perror("open dir error");exit(-1);}while((file = readdir(dir)) != NULL)//统计上传的文件数目{if((strcmp(file->d_name,"..") != 0) && (strcmp(file->d_name,".") != 0))total_file++;}closedir(dir);sprintf(buffer, "%d", total_file);send(cfd, buffer, strlen(buffer), 0);//给客户端发送要下载的文件数目recv(cfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合if(!(dir = opendir(file_name))){perror("open dir error");exit(-1);}while((file = readdir(dir)) != NULL)//开始发送目录文件{if((strcmp(file->d_name,"..") != 0) && (strcmp(file->d_name,".") != 0)){strcpy(file_name1, file_name);strcat(file_name1, "/");strcat(file_name1, file->d_name);//目录中要发送的文件的完整路径strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_clientinfo/");strcat(buffer, basename(file_name));strcat(buffer, "/");strcat(buffer, file->d_name);//客户端存放该文件的完整路径以及文件名send(cfd, buffer, strlen(buffer), 0);recv(cfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合FILE *fp = fopen(file_name1, "r");if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = fread(buffer, sizeof(char), BUFFER_SIZE, fp);send(cfd, buffer, r_size, 0);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}recv(cfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合}}closedir(dir);}else//下载普通文件{send(cfd, "file", 4, 0);//告诉客户端这是普通文件FILE *fp = fopen(buffer, "r");recv(cfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合if(NULL == fp) perror("file open failed");else{int r_size = 0;do{r_size = fread(buffer, sizeof(char), BUFFER_SIZE, fp);send(cfd, buffer, r_size, 0);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}}bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, 1, 0);//接收指令if(strcmp(buffer, "1") == 0)Upload(cfd, id, password);else if(strcmp(buffer, "2") == 0)Download(cfd, id, password);else if(strcmp(buffer, "3") == 0)Browse(cfd, id, password);else{struct sockaddr_in addr;//获取下线的客户机的IP和PORTint length = sizeof(addr);getpeername(cfd, (struct sockaddr*)&addr, &length);printf("客户机成功下线 IP: %s   PORT: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));close(cfd);}
}//用户浏览
void Browse(int cfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE], file_name[BUFFER_SIZE];while(1){bzero(buffer, BUFFER_SIZE);bzero(file_name, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//接收来自客户端的指令strcpy(file_name, buffer);if(strcmp(buffer, "-a") == 0)//浏览所有该用户的文件{strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, id);//服务端存放该用户文件的位置DIR *dir;struct dirent *file;if(!(dir = opendir(buffer))){perror("open dir error");exit(-1);}strcpy(buffer, "/");strcat(buffer, id);strcat(buffer, "/");strcat(buffer, "\n");while((file = readdir(dir)) != NULL){if((strcmp(file->d_name,"..") != 0) && (strcmp(file->d_name,".") != 0)){strcat(buffer, file->d_name);strcat(buffer, "\n");}}closedir(dir);send(cfd, buffer, strlen(buffer), 0);//给客户端发送该用户的所有文件名}else if(strcmp(buffer, "-m") == 0)//跳出循环,显示菜单break;else//浏览指定文件的属性{strcpy(file_name, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(file_name, id);strcat(file_name, "/");strcat(file_name, buffer);//服务端存放该文件的位置struct stat st;//将所有文件属性信息整合成字符串返回给客户端stat(file_name, &st);char tmp[BUFFER_SIZE];strcpy(buffer, "file type & mode (permissions): ");sprintf(tmp, "%d", st.st_mode);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "i-node number (serial number): ");sprintf(tmp, "%ld", st.st_ino);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "device number (file system): ");sprintf(tmp, "%ld", st.st_dev);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "device number for special files: ");sprintf(tmp, "%ld", st.st_rdev);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "number of links: ");sprintf(tmp, "%ld", st.st_nlink);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "user ID of owner: ");sprintf(tmp, "%d", st.st_uid);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "group ID of owner: ");sprintf(tmp, "%d", st.st_gid);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "size in bytes, for regular files: ");sprintf(tmp, "%ld", st.st_size);strcat(buffer, tmp);strcat(buffer, "\n");struct tm* accesstime = localtime(&(st.st_atime));struct tm* modifytime = localtime(&(st.st_mtime));struct tm* changetime = localtime(&(st.st_ctime));char abuffer[64], mbuffer[64], cbuffer[64];strftime(abuffer, 64, "%Y-%m-%d %H:%M:%S \n", accesstime);strftime(mbuffer, 64, "%Y-%m-%d %H:%M:%S \n", modifytime);strftime(cbuffer, 64, "%Y-%m-%d %H:%M:%S \n", changetime);strcat(buffer, "time of last access: ");strcat(buffer, abuffer);strcat(buffer, "time of last modification: ");strcat(buffer, mbuffer);strcat(buffer, "time of last file status change: ");strcat(buffer, cbuffer);strcat(buffer, "best I/O block size: ");sprintf(tmp, "%ld", st.st_blksize);strcat(buffer, tmp);strcat(buffer, "\n");strcat(buffer, "number of disk blocks allocated: ");sprintf(tmp, "%ld", st.st_blocks);strcat(buffer, tmp);strcat(buffer, "\n\n");send(cfd, buffer, strlen(buffer), 0);}}bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, 1, 0);//接收指令if(strcmp(buffer, "1") == 0)Upload(cfd, id, password);else if(strcmp(buffer, "2") == 0)Download(cfd, id, password);else if(strcmp(buffer, "3") == 0)Browse(cfd, id, password);else{struct sockaddr_in addr;//获取下线的客户机的IP和PORTint length = sizeof(addr);getpeername(cfd, (struct sockaddr*)&addr, &length);printf("客户机成功下线 IP: %s   PORT: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));close(cfd);}
}//用户起始执行函数
void *Client_start_func(void *argv)
{int cfd = *(int*)(argv);char buffer[BUFFER_SIZE], id1[6], password1[6];Read_from_file();//从文件读入数据bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//接收用户账号strcpy(id1, buffer);int i;for(i = 0; i < current_user; i++)//验证是否存在该用户{if(strcmp(User[i].id, buffer) == 0){reconfirm:recv(cfd, buffer, BUFFER_SIZE, 0);//接收该用户密码,并验证是否正确strcpy(password1, buffer);if(strcmp(User[i].password, buffer) == 0){send(cfd, "true", 4, 0);//发送给客户端说明密码正确break;}else{send(cfd, "false", 5, 0);//发送给客户端说明密码错误goto reconfirm;          //重新验证密码}}}if(i == current_user)//新用户自动注册{send(cfd, "true", 4, 0);//“说明密码正确”current_user++;//用户数加1strcpy(User[i].id, id1);strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, User[i].id);mkdir(buffer, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);//为新用户创建专属文件夹bzero(buffer, BUFFER_SIZE);recv(cfd, buffer, BUFFER_SIZE, 0);//接收新用户密码strcpy(password1, buffer);strcpy(User[i].password, buffer);Write_to_file();//向文件写入新用户账号、密码}bzero(buffer, BUFFER_SIZE);int re = recv(cfd, buffer, BUFFER_SIZE, 0);//接收指令if(strcmp(buffer, "1") == 0)Upload(cfd, id1, password1);else if(strcmp(buffer, "2") == 0)Download(cfd, id1, password1);else if(strcmp(buffer, "3") == 0)Browse(cfd, id1, password1);else{struct sockaddr_in addr;//获取下线的客户机的IP和PORTint length = sizeof(addr);getpeername(cfd, (struct sockaddr*)&addr, &length);printf("客户机成功下线 IP: %s   PORT: %d\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));close(cfd);}
}//主函数
int main()
{if(daemon(1, 1) == -1)perror("daemom error");int sfd = socket(AF_INET, SOCK_STREAM ,0);if(sfd == -1){perror("socket error");return -1;}struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);addr.sin_addr.s_addr = htonl(INADDR_ANY);socklen_t addrlen = sizeof(addr);int ret = bind(sfd, (struct sockaddr*)(&addr), addrlen);if(ret == -1){perror("bind error");return -1;}if(listen(sfd, MAX_ONLINE_CLIENT) == -1){perror("listen error");return -1;}printf("正在等待客户机连接...\n");while(1){struct sockaddr_in caddr;pthread_t pthread_id;socklen_t len = sizeof(caddr);int cfd = accept(sfd, (struct sockaddr*)(&caddr), &len);if(cfd == -1){perror("accept error");continue;}printf("客户机成功连接 IP: %s   PORT: %hu\n", inet_ntoa(caddr.sin_addr),  ntohs(caddr.sin_port));ret = pthread_create(&pthread_id, NULL, Client_start_func, (void*)&cfd);if(ret != 0){perror("pthread_create error");continue;}}
}

5.2客户端

/*高级网络程序设计课程设计1.模仿百度网盘实现一个文件上传、下载、浏览的终端网盘;2.能够实现文件和目录的存储;3.在终端下运行服务器和客户端,不对GUI做要求;4.以用户的形式组织用户数据,不同用户“登录”可以访问相应的数据;5.同一用户可以在不同位置进行登录,并且需要考虑访问冲突的问题:即用户在写一个文件的时候,该文件不能被伤处;6.浏览方法的显示参考下面的方式进行显示,同时需要根据参数选择显示文件的属性;7.写一个测试程序模拟在多进程环境下对服务器程序进行性能测试,需要的数据至少包括多少用户的情况下得到的传输率分别为多少;8.服务端程序应以Daemon的方式予以实现;9.源代码和报告提交时间:2020年7月20日。此程序为客户端*/#include <unistd.h>    //read write close
#include <libgen.h>    //basename
#include <stdio.h>     //printf
#include <stdlib.h>    //exit
#include <string.h>    //string
#include <pthread.h>   //pthread_crate
#include <sys/socket.h>//socket
#include <arpa/inet.h> //socket
#include <netinet/in.h>//socket
#include <dirent.h>
#include <sys/stat.h>#define SERVER_PORT 8080          //服务器端口
#define SERVER_IP "192.168.61.173"//服务器IP地址
#define BUFFER_SIZE 1024          //发送接收缓冲区大小void Upload(int sfd, char id[6], char password[6]);
void Download(int sfd, char id[6], char password[6]);
void Browse(int sfd, char id[6], char password[6]);//上传
void Upload(int sfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE], file_name[BUFFER_SIZE];printf("请输入你想要上传的文件:");//包括路径scanf("%s", file_name);struct stat s_buf;//用于判断该文件是目录还是普通文件stat(file_name, &s_buf);//获取文件信息,并放到s_buf中if(S_ISDIR(s_buf.st_mode))//如果是目录{send(sfd, "dir", 3, 0);//告诉服务端这是目录recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, id);strcat(buffer, "/");strcat(buffer, basename(file_name));//basename函数获取文件路径最后的文件名字,不验证路径存在与否send(sfd, buffer, strlen(buffer), 0);//给服务端发送服务端存放该目录文件的完整路径recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合DIR *dir;struct dirent *file;if(!(dir = opendir(file_name))){perror("open dir error");exit(-1);}char file_name1[BUFFER_SIZE];int total_file = 0;//目录里面的文件数while((file = readdir(dir)) != NULL)//统计上传的文件数目{if((strcmp(file->d_name,"..") != 0) && (strcmp(file->d_name,".") != 0))total_file++;}closedir(dir);sprintf(buffer, "%d", total_file);//将整型转化为字符串send(sfd, buffer, strlen(buffer), 0);//给服务端发送要接收的文件数目recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合if(!(dir = opendir(file_name))){perror("open dir error");exit(-1);}while((file = readdir(dir)) != NULL)//开始发送文件{if((strcmp(file->d_name,"..") != 0) && (strcmp(file->d_name,".") != 0)){strcpy(file_name1, file_name);strcat(file_name1, "/");strcat(file_name1, file->d_name);//目录中要上传的文件的完整路径strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, id);strcat(buffer, "/");strcat(buffer, basename(file_name));strcat(buffer, "/");strcat(buffer, file->d_name);//服务端存放该文件的完整路径以及文件名send(sfd, buffer, strlen(buffer), 0);recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合FILE *fp = fopen(file_name1, "r");if(fp == NULL)perror("file open failed");else{int r_size = 0;bzero(buffer, BUFFER_SIZE);do{r_size = fread(buffer, sizeof(char), BUFFER_SIZE, fp);send(sfd, buffer, r_size, 0);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合}}printf("目录上传成功\n");closedir(dir); }else//如果是普通文件{send(sfd, "file", 4, 0);//告诉服务端这是普通文件recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, id);strcat(buffer, "/");strcat(buffer, basename(file_name));send(sfd, buffer, strlen(buffer), 0);//给服务端发送服务端存放该文件的完整路径以及文件名recv(sfd, buffer, BUFFER_SIZE, 0);//无价值信息,仅仅是为了防止由于连续send导致发送的信息重合FILE *fp = fopen(file_name, "r");if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = fread(buffer, sizeof(char), BUFFER_SIZE, fp);send(sfd, buffer, r_size, 0);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;printf("文件上传成功!\n");}}printf("\n1、继续上传文件\n2、下载文件\n3、浏览文件\n4、退出\n请输入指令:");reinputorder:scanf("%s", buffer);if(strcmp(buffer, "1") == 0){send(sfd, buffer, 1, 0);//发送指令给服务器Upload(sfd, id, password);;}else if(strcmp(buffer, "2") == 0){send(sfd, buffer, 1, 0);Download(sfd, id, password);}else if(strcmp(buffer, "3") == 0){send(sfd, buffer, 1, 0);Browse(sfd, id, password);}else if(strcmp(buffer, "4") == 0){send(sfd, buffer, 1, 0);exit(0);}else{printf("请输入正确指令!");goto reinputorder;//重新输入指令}
}//下载
void Download(int sfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE], file_name[BUFFER_SIZE], file_name1[BUFFER_SIZE];printf("请输入你想要下载的文件名:");scanf("%s", file_name);strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_serverinfo/");strcat(buffer, id);strcat(buffer, "/");strcat(buffer, file_name);send(sfd, buffer, strlen(buffer), 0);//下载的文件在服务端的完整路径int r_len = recv(sfd, buffer, BUFFER_SIZE, 0);//下载的文件是普通文件还是目录buffer[r_len] = '\0';if(strcmp(buffer, "dir") == 0)//如果是目录{strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_clientinfo/");strcat(buffer, file_name);mkdir(buffer, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);//为下载的目录文件创建目录send(sfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合int total_file = 0, i;//目录里面的文件数recv(sfd, buffer, BUFFER_SIZE, 0);total_file = atoi(buffer);send(sfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合for(i = 0; i < total_file; i++){bzero(buffer, BUFFER_SIZE);recv(sfd, buffer, BUFFER_SIZE, 0);//存放该文件的完整路径以及文件名send(sfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合FILE *fp = fopen(buffer, "w");//打开文件,准备写入if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = recv(sfd, buffer, BUFFER_SIZE, 0);fwrite(buffer, sizeof(char), r_size, fp);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}send(sfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合}}else//如果是普通文件{send(sfd, "1", 1, 0);//无价值的信息,仅仅是为了防止由于连续recv导致接收的信息重合strcpy(buffer, "/home/zhuhezhang/UNIX课程/net_disk_clientinfo/");strcat(buffer, file_name);FILE *fp = fopen(buffer, "w");if(fp == NULL)perror("file open failed");else{int r_size = 0;do{r_size = recv(sfd, buffer, BUFFER_SIZE, 0);fwrite(buffer, sizeof(char), r_size, fp);}while(r_size == BUFFER_SIZE);fclose(fp);fp = NULL;}printf("文件下载成功!\n");}printf("\n1、上传文件\n2、继续下载文件\n3、浏览文件\n4、退出\n请输入指令:");reinputorder:scanf("%s", buffer);if(strcmp(buffer, "1") == 0){send(sfd, buffer, 1, 0);//发送指令给服务器Upload(sfd, id, password);}else if(strcmp(buffer, "2") == 0){send(sfd, buffer, 1, 0);Download(sfd, id, password);}else if(strcmp(buffer, "3") == 0){send(sfd, buffer, 1, 0);Browse(sfd, id, password);}else if(strcmp(buffer, "4") == 0){send(sfd, buffer, 1, 0);exit(0);}else{printf("请输入正确指令!");goto reinputorder;//重新输入指令}
}//浏览
void Browse(int sfd, char id[6], char password[6])
{char buffer[BUFFER_SIZE];while(1){bzero(buffer, BUFFER_SIZE);printf("请输入浏览指令:");//-a显示所有文件,-m显示菜单,输入文件名返回该文件属性信息scanf("%s", buffer);send(sfd, buffer, strlen(buffer), 0);if(strcmp(buffer, "-a") == 0)//浏览所有文件{recv(sfd, buffer, BUFFER_SIZE, 0);printf("%s\n", buffer);}else if(strcmp(buffer, "-m") == 0)break;else//浏览指定文件属性{recv(sfd, buffer, BUFFER_SIZE, 0);//接收文件属性信息printf("%s", buffer);}}bzero(buffer, BUFFER_SIZE);printf("\n1、上传文件\n2、下载文件\n3、继续浏览文件\n4、退出\n请输入指令:");reinputorder:scanf("%s", buffer);if(strcmp(buffer, "1") == 0){send(sfd, buffer, 1, 0);//发送指令给服务器Upload(sfd, id, password);}else if(strcmp(buffer, "2") == 0){send(sfd, buffer, 1, 0);Download(sfd, id, password);}else if(strcmp(buffer, "3") == 0){send(sfd, buffer, 1, 0);Browse(sfd, id, password);}else if(strcmp(buffer, "4") == 0){send(sfd, buffer, 1, 0);exit(0);}else{printf("请输入正确指令!");goto reinputorder;//重新输入指令}
}//用户起始执行函数
void Client_start_func(int sfd)
{char buffer[BUFFER_SIZE], id[6], password[6];//缓冲区、账号、密码printf("欢迎进入朱和章的模拟百度网盘系统\n");printf("           账号:");scanf("%s", id);send(sfd, id, 5, 0);reconfirm:printf("           密码:");scanf("%s", password);send(sfd, password, 5, 0);bzero(buffer, BUFFER_SIZE);recv(sfd, buffer, BUFFER_SIZE, 0);//验证密码是否正确if(strcmp(buffer, "true") == 0)//密码正确{printf("\n1、上传文件\n2、下载文件\n3、浏览文件\n4、退出\n请输入指令:");reinputorder:scanf("%s", buffer);if(strcmp(buffer, "1") == 0){send(sfd, buffer, 1, 0);//发送指令给服务器Upload(sfd, id, password);}else if(strcmp(buffer, "2") == 0){send(sfd, buffer, 1, 0);Download(sfd, id, password);}else if(strcmp(buffer, "3") == 0){send(sfd, buffer, 1, 0);Browse(sfd, id, password);}else if(strcmp(buffer, "4") == 0){send(sfd, buffer, 1, 0);exit(0);}else{printf("请输入正确指令!");goto reinputorder;//重新输入指令}}else//密码错误{printf("密码错误!请重新输入!\n");goto reconfirm;//重新输入密码}
}//主函数
int main(void)
{int sfd;struct sockaddr_in addr;if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){perror("socket error");exit(-1);}addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);addr.sin_addr.s_addr = inet_addr(SERVER_IP);if(connect(sfd, (const struct sockaddr*)(&addr), sizeof(addr)) == -1){perror("connect error");exit(-1);}Client_start_func(sfd);//用户起始执行函数return 0;
}

linux环境下,模拟百度网盘上传、下载文件相关推荐

  1. linux下载百度命令行,Linux 命令行使用百度网盘上传下载文件

    BaiduPCS 是C/C++写的一个百度网盘工具,可以在linux终端中使用. 这是通过分析网盘网站得到的直接接口,不需要创建应用. 支持多线程下载和下载时断点续传. 支持快速上传和多线程分片上传. ...

  2. java 百度网盘上传_使用pcs api往免费的百度网盘上传下载文件的方法

    百度个人云盘空间大,完全免费,而且提供了pcs api供调用操作文件,在平时的项目里往里面保存一些文件是很实用的. 环境准备: 开通读写网盘的权限及获取access_token:http://blog ...

  3. linux百度网盘上传下载bypy

    linux百度网盘上传下载bypy 安装bypy 链接百度云 上传文件或文件夹 下载文件或文件夹 关闭链接.退出授权 其他操作 安装bypy pip install bypy 链接百度云 bypy i ...

  4. 使用百度网盘上传大文件到云服务器

    因为需要把几个7G大小左右的数据上传至服务器,但无奈使用的是共享服务器,上传速度非常慢.管理员建议可以用奶牛快传(目前收费)中转,百度搜了一下,百度网盘有相同作用,正好有会员,就使用了百度网盘来传,速 ...

  5. 在Linux环境下通过百度网盘下载并安装matlab2017a

    前言 百度网盘下载matlab2017a安装包 方式1:安装百度网盘的python程序bypy 方式2:直接在浏览器下载 安装matlab 备注 参考链接 前言 最近参加一个比赛,需要在官方公布的li ...

  6. linux下使用百度网盘不限速下载文件超简超好用

    超简单超好用不限速可持续使用的神器: 下载链接可参考github链接:https://github.com/liuzhuoling2011/baidupcs-web/releases ps:可多尝试几 ...

  7. linux环境编程 百度云,linux环境下使用百度云网盘

    linux环境下使用百度云网盘 linux下经常需要备份一些文件到云端,现在能用的也就只有度娘的百度云网盘了,在github上发现一个挺好的项目,bypy,用来在linux下使用百度云. 项目地址:h ...

  8. 百度网盘上传慢的解决方法

    百度网盘是现在大部分人都在使用的网盘,可以存储图片.文件.信息等等资源,但是大家不满的主要是说下载速度.上传速度太慢了,有什么办法可以改善吗?下面小编就给大家介绍两个方法. 百度网盘上传速度太慢怎么解 ...

  9. python 网盘上传_python学习笔记 day32 实现网盘上传下载功能

    1. 作业需求 借助socket模块实现server端和client端的交互,拟实现网盘上传下载的功能: 上传: client端发送请求,把本地的文件上传给server端,server端负责接收,然后 ...

最新文章

  1. Redis 笔记(08)— 事务(一次执行多条命令、命令 watch/multi/exec/discard、错误处理)
  2. RGB色转灰度色算法
  3. 【DIY】最简单粗暴便宜的DIY定时器方法,没有之一
  4. php 二进制权限,基于二进制位的权限控制(数据库里的二进制)
  5. 收藏 | 卷积神经网络中十大拍案叫绝的操作
  6. Java基础学习总结(121)——Java JVM执行流程
  7. 【腾讯bugly干货】QQ空间直播秒开优化实践
  8. 雷军这么努力,为什么还是干不过 OV?
  9. 【论文解读】EMNLP2019-多粒度自注意力机制
  10. Node:项目文件使用async报错var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _calle
  11. paip.python错误解决18
  12. mysql2000清除挂起工具,安装SQL提示挂起操作解决方法
  13. 计算机培训短期速成班,【电脑基础班、速成班、短期班、计算机一级培训班】价格,厂家,电子商务-搜了网...
  14. Teleport Ultra网站静态资源下载工具
  15. windows的mysql无密码登录,windows mysql 跳过登录密码重置
  16. Linux中ls颜色含义
  17. mac os sierra卸载java_如何删除Install macOS High Sierra.app
  18. 微信读书增长策略:“无限卡会员业务”
  19. 《中国垒球协会》:新春贺词
  20. mysqlbinlog恢复mysql表数据

热门文章

  1. 他说:“只是单纯的想用Python收集一些素颜照,做机器学习使用”,“我信你个鬼!”
  2. 神武服务器物品开放,《神武4》电脑版庭院装饰游乐·对弈棋盘限服开放
  3. python爬虫实战笔记——爬取图书信息(利用selenium库+chromedriver.exe插件)
  4. 鼎信设备对接VOS系统_解密消防系统中的防爆设备
  5. 微软开发主管临别诤言
  6. 6岁学编程,10岁拒绝谷歌offer,神奇女孩要自己当CEO!永远别低估孩子的潜能…...
  7. 1060显卡支持dx12吗_RTX2070和GTX1080到底谁强?全面测评七彩虹Neptune RTX2070一体水冷显卡...
  8. 如何进行性能测试(在百度工作时日常压测总结)
  9. CGCTF maze
  10. 元旦主题HTML,一年级元旦主题活动方案最新