Linux高级开发课程设计报告课程设计题目:Linux下基于socket的文件传输程序设计学院:________信息工程学院_____________专业班级:________网络工程_____________年级:________级_____________________姓名:____________________________学号:________201______________完成时间:___2015___年____12___月_____25__日成绩:__________________________________指导教师:____________________________指导教师评定成绩:指导教师签名:年月日指导教师评定成绩:指导教师签名:年月日摘要线程(thread)技术早在60年代就被提出,但真正应用线程到操作系统中去,是在80年代中期。
为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?使用多线程的理由之一是和进程相比,它是一种非常"节俭"的多任务操作方式。
在Linux系统下,启动一个新的进程必须分配独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段。
而运行于一个进程中的多个线程,它们之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程间所需要的时间。
使用多线程的理由之二是线程间方便的通信机制。
对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式费时且很不方便。
由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这样快且方便。
在计算机中,凡是提供服务的一方我们称为服务端(Server),而接受服务的另一方我们称作客户端(Client)。
不过客户端及伺服端的关系不见得一定建立在两台分开的机器上,提供服务的伺服端及接受服务的客户端也有可能都在同一台机器上,这样在同一台机器上就同时扮演伺服端及客户端。
线程间方便的通信机制可以使得在我们在服务端和客户端方便的进行通信传输与各种操作,可以通过运用多线程机制方便实现上传、下载文件;增加、删除用户;以及在服务端进行文件的管理。
关键字:多线程、socket通信、服务器和客户端·1需求分析这次课程设计的要求是在以Linux为内核的操作系统下,实现多线程文件传输系统功能模块。
系统模块分为服务器和客户端两部分,客户端实现对文件的上传、下载和查看服务器默认路径下的文件列表;服务器可以对文件进行管理操作,包括创建、删除和重命名等。
多线程文件传输是一种一对多或者多对多的关系,一般是一个服务器对应着多个客户端。
客户端通过socket连接服务器,服务器要为客户端创建一个单独进程(线程)监听每个客户端的请求。
创建好连接之后文件就可以通过流的形式传输。
linux内核中为我们提供了两种不同形式的读写流,包括read()、write()和send()、recv()。
客户机对文件的查看指令也是通过流传递给服务器,服务器根据请求类型返回不同相应流。
根据socket原理和特点绘画出链接流程图,将客户机与服务器的相互通信划分为不同的模块,每个模块负责独立的功能项。
服务器输入指令管理目录下的文件,create filename是创建文件命令,rename oldname newname是删除文命令,delete filename 是删除文件命令,同时监听着客户端的请求;客户端向服务器发送上传、下载和查看请求,从而得到不同的相应,包括将文件下载到当前路径下,从当前路径下上传文件给服务器,列出服务器的文件列表。
·2 socket 通信原理国际标准化组织(ISO)在1978年提出开放系统互连参考模型(OSI:open system interconnection reference mode),该模型是设计和描述网络通信的基本框架。
OSI 采用分层的额结构化技术将通信网络分为7层,从低到高为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
TCP/IP 参考模型是由美国国防部创建,且发展至今最成功的通信协议模型,与OSI 模型对应,它将网络功能分为4层,包括网络接口层、网络层、传输层和应用层,每一层都有对应的协议。
在传输层的主要协议是TCP 协议和UDP 协议。
socket 连接就是基于TCP 协议。
TCP 是一种可靠地数据传输协议。
socket 是一种套接口,它把网络地址和端口号信息放在一个结构体中,也就是套接字地址结构。
结构图如下:通用套接口地址数据结构定义在<sys/socket.h>头文件中,形式如下: struct sockaddr {uint8_t sa_len;sa_family_t sa_family; char sa_data[14]; };IPv4套接口地址数据结构以socketaddr_in 命名,定义在<netinet/in.h>头文件中,形式如下: struct socketaddr_in {unit8_t sin_len;sa_family_t sin_family; in_port_t sin_port;struct in_addr sin_addr; unsigned char sin_zero[8]; }下图是TCP 套接口通信工作流程图:通信工作的大致流程:1) 服务器先用socket()函数来建立一个套接口,用这个套接口完成通信的监听及数据的收发。
2) 服务器用bind()函数来绑定一个端口号和ip 地址,是套接口与指定的端口号和ip 关联。
3) 服务器调用linsten()函数,是服务器的端口和Ip 处于监听状态,等待网络中某一个客户机的连接请求。
4) 客户机用socket()函数建立一个套接口,设定远程ip 和端口 5) 客户机调用connect()函数连接远程计算机指定的端口。
6) 服务器调用accept()函数来接受远程计算机的连接请求,建立起与客户机之间的通信连接。
7) 建立连接之后,客户机用write()函数(或send())想socket 中写入数据。
也可以用read()函数(或recv()函数)赌气服务器发送来的数据。
8) 服务器用read()函数(或recv()函数)来读取客户机发来的数据,也可以用write()函数(或send()函数)来发送数据。
9) 完成通信以后,使用close()函数关闭socket 连接。
·3详细设计过程·3.1服务器端创建监听与文件管理服务器负责的功能模块主要有两部分,一是对连接进来客户端所有线程的管理和服务器目录下的文件管理;二是创建线程来单独监听客户端的动作。
为了便于管理,我们创建两个user.txt和client.txt两个文档来分别负责服务器的连接和客户端的连接。
user.txt中存放了服务器名和密码。
client.txt存放了连接客户端名字和密码。
我们首先对服务器的创建有个监测,即在启动时先核实服务器的所有者username和密码password,将输入的用户、密码与user.txt中的用户密码比较,匹配成功则同意启动,否则return -1表失败。
接着创建一个socket套接口,绑定Ip设置客户端的最大连接数为10,然后创建一个sever线程来实现对服务器本身监听动作。
主体代码见最后接下来创建线程完成对客户端的监听监听等待连接:while(1){sockdata = accept(sockfd,(struct sockaddr*)0,(int*)0);…………….我们定义结构体:struct client_t{pthread_t tid;int conn_fd;int used;char name[20];}p_client[10];来存放每个客户端的socket信息、线程标识、使用号、连接号和客户名。
创建线程实现单独监听:p_client[i].conn_fd = sockdata;p_client[i].used = i;strcpy(p_client[i].name , client_name);pthread_create(&p_client[i].tid,NULL,&client_conn,&p_client[i])接下来是线程client_conn()的功能监听客户端的功能完成。
·3.2客户端连接与文件传输在客户端这边我们同样适用了检测机制,运行客户机时要将用户名、密码以及ip地址和端口号作为参数输进来,先建立与服务器的连接,然后将用户名和密码发送到服务端检测,如果检测失败则接收到一条拒绝信息,连接断开,如果检测成功则接收到一条确认信息,双方通信开始。
主体代码见最后:·4结果演示创建,改名,与删除客服端响应并连接服务器上传下载具体代码如下:服务器:#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<sys/stat.h>#include<arpa/inet.h>#include <sys/resource.h>#include <sys/types.h>#include <dirent.h>#define MAXBUF 256/*-------start of fileList functions----------*/ int fileSize(char fileName[]);//文件信息typedef struct fileinfo{char name[256];char fullName[1024];int size;time_t mod_time;char type[10];}fileinfo;//文件列表typedef struct filelist{fileinfo file;struct filelist* nextfile;}fileList;//function getfilelist//输入目录名//输出目录下的文件列表头指针fileList * getFileList(char name[1024]){fileList *head=NULL;fileList *cur=NULL;char name_temp[1024];//目录DIR * dir;//目录环境struct dirent *dir_env;//文件描述struct stat stat_file;//初始化headhead =(fileList*)malloc(sizeof(fileList));head->nextfile = NULL;//打开目录dir=opendir(name);while(dir_env=readdir(dir))//读文件描述表{//排除.和..if(strcmp(dir_env->d_name,".")==0 || strcmp(dir_env->d_name,"..")==0)continue;//把文件全名保存到新变量strcpy(name_temp,name);strcat(name_temp,dir_env->d_name);stat(name_temp,&stat_file);//获取文件描述信息//将文件信息存放到链表中//产生临时节点cur=(fileList*)malloc(sizeof(fileList));//cur赋值//文件名,fullName=cur_dir+"name";strcpy(cur->,dir_env->d_name);strcpy(cur->file.fullName,name_temp);//文件大小//文件类型if( S_ISDIR(stat_file.st_mode)){cur->file.size = 0;strcpy(cur->file.type,"mulu");strcat(cur->file.fullName,"/");}else{cur->file.size = stat_file.st_size;strcpy(cur->file.type,"file");}//修改日期cur->file.mod_time = ctime(&stat_file.st_mtime); //将临时节点插入head中if(head->nextfile ==NULL){head->nextfile = cur;cur->nextfile = NULL;}else{cur->nextfile = head->nextfile;head->nextfile = cur;}}return head;}//showAllNode//输入:目录//输出:次目录下所有的文件,和所有目录之下的文件void showAllNode(fileList *head){fileList * temp;//数组索引int i=0,j=0;//如果head为空,直接返回fileList * headArray[1024];if(head == NULL)return ;//输出当前目录printf("%s ",head->file.fullName);printf("\n");//输出head中的文件temp =head->nextfile;char fileListString[MAXBUF];FILE *file;char _temp[30];strcpy(_temp,"temp.txt");file=fopen(_temp,"w");if(file==NULL){printf("The file is created failed!");exit(1);}while(temp){//判断是否为文件,是文件显示文件//若为目录,将目录名放入队列,求队列目录if (strcmp(temp->file.type,"file")==0){bzero(fileListString,MAXBUF);printf("file:%s ",temp->file.fullName);strcat(fileListString,temp->file.fullName);strcat(fileListString,"\n");while ((strlen(fileListString)) > 0){int write_length = fwrite(fileListString, sizeof(char), strlen(fileListString), file);if (write_length < strlen(fileListString)){printf("File Write into Failed\n");break;}bzero(fileListString, MAXBUF);}}else{if(i>=1024){printf("there are too many direcotry\n");return;}//头节点初始化headArray[i] = getFileList(temp->file.fullName);//头节点名称strcpy(headArray[i]->file.fullName,temp->file.fullName); i++;}temp=temp->nextfile;}fclose(file);//对目录队列中目录使用递归,直到结束for(j=0;j<i;j++)showAllNode(headArray[j]);return ;}//showList//输入:列表头//输出:显示列表,返回voidvoid showList(fileList * head){//判断head 是否为空,若为空直接返回if(head == NULL)return;//若不为空则显示它的内容while(head){printf("%s\n",head->file.fullName);head = head->nextfile;}return ;}/*----------end of fileList functions-----------*/void main(){int opt=1;while(opt!=0){printf("Please choose your choice bellow:\n");printf("1:Manage the files.\n");printf("2:Connect the clients.\n");char window[2];scanf("%s",window);if((strncmp(window,"1",1))==0){printf("Please input your choice bellow:\n");printf("1: Create a new file.\n");printf("2: Delete a file.\n");printf("3: Rename a known file.\n");char choice[2];scanf("%s",choice);if((strncmp(choice,"1",1))==0){printf("Please input the new file name:");char filename[20];scanf("%s",filename);FILE *file;file=fopen(filename,"w");if(file==NULL){printf("The file created failed!\n");}else{printf("The file has created successfully.\n");}continue;}else if((strncmp(choice,"2",1))==0){printf("Please input the file name with the file path you want to delete:\n");char filename[20];scanf("%s",filename);remove(filename);printf("The file has been deleted successfully.\n");continue;}else{printf("Please input the file name you want to rename:\n");char _old[20];scanf("%s",_old);printf("Please input the new file name:\n");char _new[20];scanf("%s",_new);int result = rename( _old, _new );if( result != 0 )printf( "Could not rename '%s'\n", _old );elseprintf( "File '%s' renamed to '%s'\n", _old, _new );continue;}}else{int ssock;int clen;struct sockaddr_in client_addr,server_addr;char buf[MAXBUF];if((ssock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))<0){ perror("socket error:");exit(1);}printf("Run the server successfully.\nAnd now waiting the client comming...\n");memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");server_addr.sin_port=htons(6669);if(bind(ssock,(struct sockaddr *)&server_addr,sizeof(server_addr))<0){perror("bind error:");exit(1);}int window=1;while(window!=0){clen=sizeof(client_addr);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr,&clen);printf("%s\n",buf);if((strncmp(buf, "0", 1)) == 0){if((strncmp(buf, "0yy", 7)) == 0){strcpy(buf,"yes");printf("It's username and right.\n");}else{strcpy(buf,"no");printf("It's username but wrong.\n");}sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr ,sizeof(client_addr));}else if((strncmp(buf, "1", 1)) == 0){if((strncmp(buf, "1123", 4)) == 0){strcpy(buf,"yes");sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr ,sizeof(client_addr));printf("It's password and right.\n");recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr,&clen);if((strncmp(buf, "upload", 5)) == 0){printf("The client is going to upload file...\n");recvfrom(ssock,(void *)buf,MAXBUF,0,(structsockaddr*)&client_addr,&clen);//get filenameprintf("The filename of the file uploaded by user is:%s\n",buf);FILE *file;char temp[30];strcpy(temp,"recieve/");strcat(temp,buf);file=fopen(temp,"w");if(file==NULL){printf("The file is created failed!");exit(1);}bzero(buf, MAXBUF);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr,&clen);while (strlen(buf) > 0){int write_length = fwrite(buf, sizeof(char), strlen(buf), file);if (write_length < strlen(buf)){printf("File Write into Failed\n");break;}bzero(buf, MAXBUF);}fclose(file);printf("Recieve file already success.\n");}else{printf("The client wants to download file.\n");printf("Send the filelist to the client...\n");//filelistfileList *mylist;//显示的目录char name[1024]="recieve/";//取得目录下文件//头指针传递的目录或者文件名mylist =getFileList(name);strcpy(mylist->file.fullName,name);//显示目录下文件//showList(mylist);//显示目录下所有文件showAllNode(mylist);//send fileListFILE *file;char temp[30];strcpy(temp,"temp.txt");file=fopen(temp,"r");if(file==NULL){printf("The file cannot open!");exit(1);}else{printf("\nThe fileListString open successfully!\n");bzero(buf,MAXBUF);int lengsize = 0;while((lengsize = fread(buf,1,MAXBUF,file)) > 0){printf("lengsize = %d\n",lengsize);sendto(ssock,(void*)buf,MAXBUF,0,(struct sockaddr*)&client_addr,sizeof(server_addr));printf("%s\n",buf);bzero(buf, MAXBUF);}printf("The fileListString has been sent to the client already.\n");}fclose(file);bzero(buf, MAXBUF);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr,&clen);printf("The client choosen file: %s\n",buf);strcpy(temp,"recieve/");strcat(temp,buf);file=fopen(temp,"r");if(file==NULL){printf("The file is created failed!");exit(1);}else{printf("The file open successfully!\n");printf("The file is downloading to theclient now...\n");bzero(buf,MAXBUF);int lengsize = 0;while((lengsize = fread(buf,1,MAXBUF,file)) > 0){printf("lengsize = %d\n",lengsize);sendto(ssock,(void*)buf,MAXBUF,0,(struct sockaddr*)&client_addr,sizeof(server_addr));bzero(buf, MAXBUF);}printf("The file has been downloaded already.\n");}fclose(file);exit(1);//还可以显示此时的文件目录信息,检查是否完成了上传}close(ssock);window=0;}else{strcpy(buf,"no");sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&client_addr ,sizeof(client_addr));printf("It's password but wrong.\n");}}}}}}客服端:#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<strings.h>#include<string.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#define PORT 6669#define MAXBUF 256int check_passwd(int sockfd);int tra_file(int sockfd);void main ( int argc, char *argv[]){char username[20];char password[20];char temp[21];char window;printf("Please input your selections bellow:\n");printf("1:Login the server with your username and password\n");printf("2:Registe a newly user now.\n");printf("0:Exit the system.\n");window=getchar();while(window>0){if(window=='1'){int key=0;printf("Please input your username:");scanf("%s",username);printf("%s\n",username);int ssock;int clen;struct sockaddr_in client_addr,server_addr;char buf[MAXBUF];if((ssock = socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket error:你暂时不能登录服务器server,请稍后再登录...\n");exit(1);}else{while(key==0){memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family =AF_INET;server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");server_addr.sin_port=htons(PORT);strcpy(temp,"0");strcat(temp,username);strcpy(buf,temp);sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));printf("你已经发送username(%s)给server请等待响应...\n",buf);clen=sizeof(client_addr);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,&clen);printf("你已经得到了server的响应:%s\n",buf);if((strncmp(buf, "yes", 3)) != 0){printf("The username you inputed is wrong!\n");window='1';close(ssock);printf("Please input your username:");scanf("%s",username);printf("%s\n",username);}else{close(ssock);key=1;}}}printf("Please input your password:");scanf("%s",password);printf("%s\n",password);if((ssock = socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket error:你暂时不能登录服务器server,请稍后再登录...\n");exit(1);}else{key=0;while(key==0){memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family =AF_INET;server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");server_addr.sin_port=htons(PORT);bzero(temp,21);strcpy(temp,"1");printf("%s\n",temp);strcat(temp,password);strcpy(buf,temp);sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));printf("你已经发送password(%s)给server请等待响应...\n",buf);clen=sizeof(client_addr);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,&clen);printf("你已经得到了server的响应:%s\n",buf);if((strncmp(buf, "yes", 3)) != 0){printf("The password you inputed is wrong!\n");window='1';close(ssock);printf("Please input your password:");scanf("%s",password);printf("%s\n",password);}else{//file list display and file chooseprintf("Please choose eithor upload file or download file bellow:\n");printf("1:upload file.\n");printf("2:download file.\n");char choose[2];scanf("%s",choose);char path[20];char filename[20];if((strncmp(choose,"1",1))==0){printf("please input the path you want to upload end with '/':\n");scanf("%s",path);printf("Please input the file you want to upload:\n");strcpy(buf,"upload");sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));scanf("%s",filename);strcat(path,filename);strcpy(buf,filename);sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));FILE *file;file=fopen(path,"r");if(file==NULL){perror("The file open failed!");exit(1);}else{printf("The file open successfully!\n");printf("The file is uploading now...\n");bzero(buf,MAXBUF);int lengsize = 0;while((lengsize = fread(buf,1,MAXBUF,file)) > 0){printf("lengsize = %d\n",lengsize);sendto(ssock,(void*)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));bzero(buf, MAXBUF);}printf("The file has been uploaded already.\n");exit(1);//还可以显示此时的文件目录信息,检查是否完成了上传}}else{bzero(buf,MAXBUF);strcpy(buf,"download");sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));printf("The filelist supported by server is bellow:\n");bzero(buf,MAXBUF);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,&clen);printf("%s\n",buf);printf("Please input the file you want to download :\n");scanf("%s",filename);strcpy(buf,filename);sendto(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,sizeof(server_addr));bzero(buf,MAXBUF);FILE *file;char temp[30];strcpy(temp,"download/");strcat(temp,filename);file=fopen(temp,"w");if(file==NULL){printf("The file is created failed!");exit(1);}bzero(buf, MAXBUF);recvfrom(ssock,(void *)buf,MAXBUF,0,(struct sockaddr*)&server_addr,&clen);while (strlen(buf) > 0){int write_length = fwrite(buf, sizeof(char), strlen(buf), file);printf("The write_length is:%d\n",write_length);if (write_length < strlen(buf)){printf("File Write into Failed\n");break;}bzero(buf, MAXBUF);}fclose(file);printf("Download file already success.\n");}close(ssock);key=1;window=0;}}}}else if(window=='2'){printf("Please input your newly username:");scanf("%s",username);printf("Please input your newly password:");scanf("%s",password);}else {break;}}}·5总结体会为期一周的Linux课程设计结束了,从中获益匪浅。