资源描述
摘要线程(thread )技术早在60年代就被提出,但真正应用线程到操作系统中去, 是在80年代中期。为什么有了进程的概念后,还要再引入线程呢?使用多线程 到底有哪些好处?使用多线程的理由之一是和进程相比,它是一种非常节俭”的多任务操作方 式。在Linux系统下,启动一个新的进程必须分配独立的地址空间, 建立众多的 数据表来维护它的代码段、堆栈段和数据段。而运行于一个进程中的多个线程, 它们之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远 远小于进程所花费的空间,而且,线程间彼此切换所需的时间也远远小于进程问 所需要的时间。使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具 有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式费时 且很不方便。由于同一进程下的线程之间共享数据空间, 所以一个线程的数据可 以直接为其它线程所用,这样快且方便。在计算机中,凡是提供服务的一方我们称为服务端( Server ),而接 受服务的另一方我们称作客户端(Client ) o不过客户端及伺服端的关系不见得一定建立在两台分开的机器上,提供服务的伺服端及接受服务的客 户端也有可能都在同一台机器上,这样在同一台机器上就同时扮演伺服端 及客户端。线程间方便的通信机制可以使得在我们在服务端和客户端方便的进行通信 传输与各种操作,可以通过运用多线程机制方便实现上传、下载文件;增加、删 除用户;以及在服务端进行文件的管理。关键字:多线程、socket通信、服务器和客户端, 1需求分析这次课程设计的要求是在以Linux为内核的操作系统下,实现多线程文件传 输系统功能模块。系统模块分为服务器和客户端两部分,客户端实现对文件的上传、下载和查看服务器默认路径下的文件列表;服务器可以对文件进行管理操作, 包括创建、删除和重命名等。多线程文件传输是一种一对多或者多对多的关系,一般是一个服务器对应着 多个客户端。客户端通过socket连接服务器,服务器要为客户端创建一个单独 进程(线程)监听每个客户端的请求。创建好连接之后文件就可以通过流的形式传输。linux内核中为我们提供了 两种不同形式的读写流,包括read()、write()和send()、recv()。客户机对文 件的查看指令也是通过流传递给服务器,服务器根据请求类型返回不同相应流。根据socket原理和特点绘画出链接流程图,将客户机与服务器的相互通信 划分为不同的模块,每个模块负责独立的功能项。服务器输入指令管理目录下的 文件,create filename 是创建文件命令, rename oldname newname1重命名文 命令,delete filename是删除文件命令,同时监听着客户端的请求;客户端向 服务器发送上传、下载和查看请求,从而得到不同的相应,包括将文件下载到当 前路径下,从当前路径下上传文件给服务器,列出服务器的文件列表。 2 socket 通信原理国际标准化组织(ISO)在1978年提出开放系统互连参考模型(OSI:opensystem interconnection reference mode),该模型是设计和描述网络通信的基 本框架。OSI采用分层的额结构化技术将通信网络分为7层,从低到高为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。TCP/IP参考模型是由美国国防部创建,且发展至今最成功的通信协议模型, 与OSI模型对应,它将网络功能分为4层,包括网络接口层、网络层、传输层和 应用层,每一层都有对应的协议。在传输层的主要协议是TCP协议和UDPft、议。socket连接就是基于TCPB议。TCP是一种可靠地数据传输协议。socket是一种套接口,它把网络地址和端口号信息放在一个结构体中,也就是套接字地址结构。结构图如下:Ip地址端口号168.222.222.2229999U7套接口222.222.222.2229999套接口与ip、端口号的关系通用套接口地址数据结构定义在sys/socket.h头文件中,形式如下: struct sockaddruint8_t sa_len;sa_family_t sa_family;char sa_data14;;一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_zero8;.下图是 TCP 套接口通信工作流程图客户机进程服务器进程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 name20;p_client10;来存放每个客户端的socket信息、线程标识、使用号、连接号和客户名。 创建线程实现单独监听:p_clienti.conn_fd = sockdata;p_clienti.used = i;strcpy(p_clienti.name , client_name);pthread_create(&p_clienti.tid,NULL,&client_conn,&p_clienti)接下来是线程client_conn()的功能监听客户端的功能完屏 3.2客户端连接与文件传输在客户端这边我们同样适用了检测机制,运行客户机时要将用户名、密码以 及ip地址和端口号作为参数输进来,先建立与服务器的连接,然后将用户名和 密码发送到服务端检测,如果检测失败则接收到一条拒绝信息,连接断开,如果检测成功则接收到一条确认信息,双方通信开始。主体代码见最后:到此为止我们已经实现了服务器和客户端的主体功能,具体代码查看附录文件夹。, 4结果演示编译完成的serve和clientIcon Vi ewPlates * X谪wlK Desktop_ File System-j Network_ Floppy Dr,.1 Tfdih,文档 ,音乐日褛罚L下Qdownloadfuel4srec i evesend*i.ncl LrtCl桌面出腿Wifclientclient cserverserverxtemp.txt创建文档adownloadred eveclient*it-cL$ervr更面fan clfind力口1serversatxtrerif fKlf rent wivclient cwlutuntu:-file Edit yiew Search Terminal helpPlease input your choice bellow; 工:Create a new file.2: Delete a file.3: Rename a known filerIPlease inputThe file hasthe new file name:a.txt created successfully.将文档a改名为download令 clientrecieve臬面r 口仁 i *incl *tncl findb.txtFC L reci? rie 皿七机M*incl ifinclcbent.cserverserver.ctemp.txtwtpubuntu:一Fite Edit View Search Terminal HelpPlease input a .tztPlease inputb.txtFile a.txtttie file name you warn to rename:the new file name:renamed to b.txt将 文recievelsendi/iewBookmarks Helpsend 口wlubuntu:-file Edit yiew search lerminal Hdpb.txtplease input the path you want to upload end with y : send/b. txt The file The file lengsize The filePUase input the file you want to upload; open successfully!is uploading newr.ii2 txt3.txta. txthas been uploaded already. 谓lbuntu:7 口b.txtCo Bookmarks Helpwl 典 buntu:一ii2.txt3Xxtb.txta.txt将文档 a上传由send上传至U recieve2File Edit View Search Terminal Helpplease input the path you want to upload end with /: send/Please input the file you want t。 upload: b. txtThe file open successfully!Iitie file is uploading now. rlengsize = 5The file has been uploaded already.wlubuntu:7 -并 连 接 服 务 器wlubuntu: * pnwlubuntu:一File Edit view search TemninaL Helpwlubumu:F . /serverPlease choose your choice bellow: l:Marage rhe files.2:connect the clients.File Edit View Search leminal HelpRun the server suecessful.ly.And now waiting the client tomfning. 奶Its username and right, 1123Its password and right.The client wants to (iovmlcad file. Send the filelist to the eliert.Please input your username:yy 你已拄发送username(0yy) sErver请等待师应- 柝己拄当到了 server的碇或:ye$ Please input your password; 123 1你 SEj password (1123) iS serveri#.你巳提得到Tsezer的响应:yesPlease choose eithor upload file or download file bellow: l:uplaad file.2:download file.recieve/file:recieve/2.txtThe fiteListstringLengsize = 56 recieve/2.tKt recieve/l,tKt recievt:/3.tKt necieve/a.txtfile:recieve/lLtxt open successfully!The filelist supported by server is bellows fiL recieve/2,txtrecieve/l.txtrecieve/a.txtrecieve/a.txtPlease input the file you want to download :Hie fileLLstStrlngclii陶5 been sent to ThedownloadrecievesendserverclientcserverxFfClf re-ri reci* FMlffip-cl ?iheI *incl 科几cl#incl*mci*inl下载文Co Bookmarks Hetp, w wi recievePlease input the file you want to download : 3.txtgo goakrnarks HelpOwl(pubuntu:He Edit View Search lerminal HelpFile Edit View Search Terminal Helpl:upload file.2idawnload file.The filelist supported by server is bellow: recieve/2,txt recieve/1.txtrecieve/3.1xtrecieve/a.txtThe client thoosen file: 3,txtThe file open successfullyThe file is downloading to the client nowThe file has been downloaded already.wlubunttJ:4$ 口,-l.txt3.txt文档后l.txt具体代码如下:服务器:#include#include#include#include#include#include#include#include #include #include #define MAXBUF 256/*start of fileList functions*/int fileSize(char fileName);/文件信息typedef struct fileinfochar name256;char fullName1024;int size;time_t mod_time;char type10;fileinfo;/文件列表typedef struct filelistfileinfo file;struct filelist* nextfile;押eList;/function getfilelist/输入目录名/输出目录下的文件列表头指针fileList * getFileList(char name1024)fileList *head=NULL;fileList *cur=NULL;char name_temp1024;/目录一DIR * dir;/ 目录环境struct dirent *dir_env;/文件描述一struct stat stat_file;/ 初始化headhead =(fileList*)malloc(sizeof(fileList); head-nextfile = NULL;/ 打开目录dir=opendir(name);读文件描述表IIwhile(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);/获取文件描述信息/将2件信息存版到链表中/ 产生临时节点cur=(fileList*)malloc(sizeof(fileList);/cur 赋值/ 文件名,fullName=cur_dir+name;strcpy(cur-file.name,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 * headArray1024;if(head = NULL)return ;/ 输出当前目录printf(%s ,head-file.fullName);printf(n);/ 输出head中的文件temp =head-nextfile;char fileListStringMAXBUF;FILE *file;char _temp30;strcpy(_temp,temp.txt);file=fopen(_temp,w);if(file=NULL)printf(The file is created failed!);exit;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 =1024) printf(there are too many direcotryn);return;/头节点初始化headArrayi = getFileList(temp-file.fullName);/头节点名称strcpy(headArrayi-file.fullName,temp-file.fullName);i+;temp=temp-nextfile;fclose(file);/对目录队列中目录使用递归,直到结束for(j=0;jfile.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 window2;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 choice2;scanf(%s,choice);if(strncmp(choice,1,1)=0)printf(Please input the new file name:);char filename20;scanf(%s,filename);FILE *file;file=fopen(filename,w);if(file=NULL)printf(The file created failed!n);elseprintf(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 filename20;scanf(%s,filename);remove(filename);printf(The file has been deleted successfully.n); continue; else printf(Please input the file nameyou want to rename:n); char _old20;scanf(%s”,_old);printf(Please input the new file name:n);char _new20;scanf(%s”,_new);int result = rename( _old, _new );if( result != 0 )printf( Could not rename %sn, _old ); elseprintf( File %s renamed to %sn, _old, _new );continue;elseint ssock;int clen;struct sockaddr_in client_addr,server_addr;char bufMAXBUF;if(ssock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)0) perror(socket error:);exit; printf(Run the server successfully.nAnd now waiting the client commingn);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,(structsockaddr*)&server_addr,sizeof(server_addr) 0) int write_length = fwrite(buf, sizeof(char), strlen(buf), file);if (write_length file.fullName,name);/显示目录下文件/showList(mylist);/显示目录下所有文件showAllNode(mylist);/send fileListFILE *file;char temp30;strcpy(temp,temp.txt);file=fopen(temp,r);if(file=NULL)printf(The file cannot open!);exit; elseprintf(nThefileListStringopensuccessfully!n);bzero(buf,MAXBUF);int lengsize = 0;while(lengsize=fread(buf,1,MAXBUF,file) 0)printf(lengsize = %dn,lengsize); sendto(ssock,(void*)buf,MAXBUF,0,(struct sockaddr*)&client_addr,sizeof(server_addr); printf(%sn,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,(structsockaddr*)&client_addr,&clen);printf(The client choosen file: %sn,buf); strcpy(temp,recieve/);strcat(temp,buf);file=fopen(temp,r);if(file=NULL)printf(The file is created failed!); exit;elseprintf(The file open successfully!n);printf(The file is downloading to the client nown);bzero(buf,MAXBUF);int lengsize = 0;while(lengsize=fread(buf,1,MAXBUF,file) 0)printf(lengsize = %dn,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,(structsockaddr*)&client_addr ,sizeof(client_addr); printf(Its password but wrong.n);客服端:#include#include#include#include#include#include#include#include#include#define PORT 6669#define MAXBUF 256int check_passwd(int sockfd);int tra_file(int sockfd);void main (int argc, char *argv口)char username20;char password20;char temp21;char window;printf(Please input your selections bellow:n);printf(1:Login the server with your username and passwordn);printf(2:Registe a newly user now.n);printf(0:Exit the system.n);window=getchar();while(window0)if(window=1)int key=0;printf(Please input your username:);scanf(%s,username);printf(%sn,username);int ssock;int clen;struct sockaddr_in client_addr,server_addr;char bufMAXBUF;if(ssock = socket(AF_INET,SOCK_DGRAM,0)0)perror(socket error:你暂时不能登录服务器server ,请稍后 再登录n);exit;elsewhile(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,(structsockaddr*)&server_addr,sizeof(server_addr); printf( 你已经发送 username(%s)给server 请等待响 应n,buf);clen=sizeof(client_addr);recvfrom(ssock,(void*)buf,MAXBUF,0,(structsockaddr*)&server_addr,&clen);printf(你已经得到了 server 的响应:sn,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(%sn,username);elseclose(ssock);key=1;printf(Please input your password:);scanf(%s,password);printf(%sn,password);if(ssock = socket(AF_INET,SOCK_DGRAM,0) 0)printf(lengsize = %dn,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);/还可以显示此时的文件目录信息,检查是否完成了上传*)buf,MAXBUF,0,(structsupported by server is*)buf,MAXBUF,0,(structthe file you want to*)buf,MAXBUF,0,(struct else bzero(buf,MAXBUF); strcpy(buf,download); sendto(ssock,(void sockaddr*)&server_addr,sizeof(server_add
展开阅读全文