linux下的简单文件服务器和客户端程序
来源:互联网 发布:写英语论文的软件 编辑:程序博客网 时间:2024/04/27 09:04
本文是我的一次作业,由于花了很多精力,记下来以后可能还会用到。代码部分是从老师那拷贝的,作业是实现代码中没有实现的put和delete命令对文件的操作。我对代码的理解都做了标注,有点乱,但阅读方便。本程序的命令要求
Dir/ls
后接字符串,列出服务器的某个目录的内容
Get
后接两个字符串,下载远程文件到本地目录
Delete
后接字符串,删除远程文件
Put
后接字符串,上传文件到远程目录
服务端和客户端都有一个user文件夹保存上传和下载的文件
本文全部代码和资源在http://download.csdn.net/detail/u012296503/9513179可下载
服务端代码server.c
#include"unp.h"#include <string.h>#include <unistd.h>#include <dirent.h>void ftpserv(int sockfd);void sig_chld(int signo);ssize_t Readline2(int fd, void *ptr, size_t maxlen);int main(int argc, char **argv){intlistenfd, connfd;pid_tchildpid; // 套接字地址结构的长度,一般为unit32_tsocklen_tclilen; // 网际套接字地址结构struct sockaddr_incliaddr, servaddr;voidsig_chld(int); // family是AF_INET表示地址族,type是SOCK_STREAM表示字节流套接字,protocol是0表示TCP传输协议 // listenfd是套接字描述符listenfd = Socket(AF_INET, SOCK_STREAM, 0); /* 以下代码,一直到listen函数,表示在待捆绑到该TCP套接字的网际接口套接字地址结构中填入 通配地址(INADDR_ANY)和服务器的端口号为21000.捆绑通配地址是在告知系统,要是系统是 多宿主机,我们将接受目的地址为任何本地接口的连接。在选择通配地址和指定端口号时,内核 选择IP地址,进程指定端口号。 */ bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(21000); // bind将一个本地协议地址赋予一个套接字,对于TCP服务器,就限定了该套接字只接受那些目的地为 // 为这个IP地址的客户连接。Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); // listen将该套接字装换成一个监听套接字,导致套接字从CLOSED状态转变成LISTEN状态。Listen(listenfd, LISTENQ); // 当SIGCHLD信号发生,函数sig_chld就会被调用Signal(SIGCHLD, sig_chld);/* must call waitpid() */for ( ; ; ){clilen = sizeof(cliaddr); if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0){if (errno == EINTR)continue;/* back to for() */elseerr_sys("accept error");}if ( (childpid = Fork()) == 0){/* child process */Close(listenfd);/* close listening socket */ftpserv(connfd);/* process the request */exit(0);}Close(connfd);/* parent closes connected socket */}}void ftpserv(int sockfd){ssize_tn;charbuf[MAXLINE];charsendline[MAXLINE];char wdpath[200]; // char * getcwd(char * buffer, size_t size);会将当前的工作目录绝对路径复制到参数buffer所指的内存空间, // 参数size 为buffer的空间大小。获得当前目录 if(getcwd(wdpath,200)==NULL)return ;strcat(wdpath,"/user");char dirname[1000];char dirlen[7];DIR *pdir;struct dirent *pent;if ((pdir = opendir(wdpath)) == NULL){fprintf(stderr, "open dir failed.\n");return;}again:while ( (n = Readline2(sockfd, buf, MAXLINE)) > 0){ //Writen(sockfd, buf, n); fprintf(stderr,"n=%d\nbuf=%s",n,buf); /*作用于字符串buf,以包含在" \n"中的字符为分界符,将buf切分成一个个子串; cmd指向第一个子串。要想遍历子串需要一个循环 while(cmd != NULL){ printf("%s",cmd); cmd = strtok(NULL," \n"); //指针后移 } */ char * cmd=strtok(buf," \n"); if( strcmp(cmd,"dir") ==0 || strcmp(cmd,"ls")==0) { char * pdirname=dirname; int lines=0; // 从目录流中读取所有的目录项,并拷贝该目录结构的d_name字段 while (1) { pent = readdir(pdir); if (pent == NULL) break; // fprintf(stderr, "%5d %s\n",pent->d_ino, pent->d_name); strcpy(pdirname,pent->d_name); strcat(pdirname,"\r\n"); // 输出目录下的各个文件 fprintf(stderr, "%d %s",strlen(pdirname), pdirname); lines++; pdirname += strlen(pent->d_name)+2; } *pdirname=0; // 将lines打印到字符串中 sprintf(dirlen,"%d\n",lines); fprintf(stderr,"lines %d\n",lines); // 将dirlen写入套接字,以便客户端可以读取,此处写入行数 Writen(sockfd, dirlen, strlen(dirlen)); // 将目录中的文件名写入套接字中,以便客户端读取 Writen(sockfd,dirname,strlen(dirname)); // 目录项归起始位置,以便下次dir命令重新读取目录项。 rewinddir(pdir); } else if(strcmp(cmd,"get")==0) { char filename[100]; // 拼接目录 strcpy(filename,wdpath); strcat(filename,"/"); // strtok(NULL," \n")是get后面的文件名 strcat(filename, strtok(NULL," \n")); FILE *fp=0; if((fp=fopen(filename,"rb"))==NULL) { fprintf(stderr,"failed open file %s",filename); return; } fprintf(stderr, "open file %s.\n",filename); long filelen; /* 定义函数 int fseek(FILE * stream,long offset,int whence); 函数说明 fseek()用来移动文件流的读写位置。参数stream为已打开的文件指针,参数offset为根据参数whence来移动读写位置的位移数。 参数 whence为下列其中一种: SEEK_SET从距文件开头offset位移量为新的读写位置。SEEK_CUR 以目前的读写位置往后增加offset个位移量。 SEEK_END将读写位置指向文件尾后再增加offset个位移量。 当whence值为SEEK_CUR 或SEEK_END时,参数offset允许负值的出现。 */ fseek(fp,0,SEEK_END); /* 定义函数 long ftell(FILE * stream); 函数说明 ftell()用来取得文件流目前的读写位置。参数stream为已打开的文件指针。 返回值 当调用成功时则返回目前的读写位置,若有错误则返回-1,errno会存放错误代码。 错误代码 EBADF 参数stream无效或可移动读写位置的文件流。 */ filelen=ftell(fp); // rewind()用来把文件流的读写位置移至文件开头 rewind(fp); char flen[7]; sprintf(flen,"%d\n",(int)filelen); fprintf(stderr,"filesize %s\n",flen); // 服务器端算出文件的大小,并写入套接字以供客户端读取 Writen(sockfd, flen, strlen(flen)); // 同时显示出来文件的大小 fprintf(stderr, "sent filesize=%s",flen); long left=filelen; /* .一般调用形式 fread(buffer,size,count,fp); fwrite(buffer,size,count,fp); 说明 (1)buffer:是一个指针,对fread来说,它是读入数据的存放地址。对fwrite来说,是要输出数据的地址。 (2)size:要读写的字节数; (3)count:要进行读写多少个size字节的数据项; (4)fp:文件型指针。 */ // 如果文件大小大于MAXLINE就分次读,然后写入套接字。 while(left>MAXLINE) { fread(sendline,MAXLINE,1,fp); Writen(sockfd, sendline, MAXLINE); left-=MAXLINE; } // 对于剩下的部分再处理 if(left<=MAXLINE) { fread(sendline,left,1,fp); Writen(sockfd, sendline, left); } fprintf(stderr, "file sent done.\n"); fclose(fp); } /**/ else if(strcmp(cmd,"put")==0){ if(Readline2(sockfd,recvline,MAXLINE) == 0){ err_quit("str_cli: client terminated prematurely"); } int bytes = atoi(recvline); fprintf(stderr,"file length = %d.\n",bytes); int left = bytes; FILE *fp = 0; if(left > 0){ char filename[100]; strcpy(filename,wdpath); strcat(filename,"/"); strcat(filename,strtok(NULL," \n")); if((fp = fopen(filename,"wb")) == NULL){ fprintf(stderr,"failed open file %s",filename); return; } fprintf(stderr,"file create: %s.\n",filename); } while(left > MAXLINE){ Readn(sockfd,recvline,MAXLINE); left -= MAXLINE; fwrite(recvline,MAXLINE,1,fp); } if(left <= MAXLINE){ Readn(sockfd,recvline,left); fwrite(recvline,left,1,fp); } fprintf(stderr,"file writen done.\n"); fclose(fp); } else if(strcmp(cmd,"delete")==0){ char filename[100]; char name[30]; strcpy(filename,wdpath); strcat(filename,"/"); strcat(filename,strtok(NULL," \n")); printf("filename is %s\n",filename); sprintf(name,"%s\n",filename); Writen(sockfd,name,strlen(name)); if(remove(filename) == 0){ fprintf(stderr,"file delete successfully.\n"); }else{ fprintf(stderr,"file don't exist.\n"); } } if (n < 0 && errno == EINTR) goto again; else if (n < 0) err_sys(" read error"); //closedir(pdir);}fprintf(stderr,"n=%d\nbuf=%s",n,buf);}void sig_chld(int signo){pid_tpid;intstat;while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)printf("child %d terminated\n", pid);return;}ssize_treadline(int fd, void *vptr, size_t maxlen){ssize_tn, rc;charc, *ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {again:if ( (rc = read(fd, &c, 1)) == 1) {*ptr++ = c;if (c == '\n')break;/* newline is stored, like fgets() */} else if (rc == 0) {*ptr = 0;return(n - 1);/* EOF, n - 1 bytes were read */} else {if (errno == EINTR)goto again;return(-1);/* error, errno set by read() */}}*ptr = 0;/* null terminate like fgets() */return(n);}/* end readline */ssize_tReadline2(int fd, void *ptr, size_t maxlen){ssize_tn;if ( (n = readline(fd, ptr, maxlen)) < 0)err_sys("readline error");return(n);}
客户端client.c
#include <stdio.h>#include <stdlib.h>#include"unp.h"void ftp_cli(FILE *fp, int sockfd);ssize_t Readline2(int fd, void *ptr, size_t maxlen);int main(int argc, char **argv){intsockfd; // 网际套接字地址结构struct sockaddr_inservaddr; if (argc != 2) err_quit("usage: tcpcli <IPaddress>"); sockfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(21000); // 将IP地址从数值形式转化为表达式形式Inet_pton(AF_INET, argv[1], &servaddr.sin_addr); // 调用connect函数是激发TCP的三路握手过程Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));ftp_cli(stdin, sockfd);/* do it all */exit(0);}void ftp_cli(FILE *fp, int sockfd){char sendline[MAXLINE], recvline[MAXLINE];char cmd [MAXLINE]; // 获得当前目录char wdpath[200];if(getcwd(wdpath,200)==NULL)return ;strcat(wdpath,"/user");for ( ; ; ){ fprintf(stderr, "myftpclient-->"); // 使用fputs和fgets从文件指针或者标准输入输出设备读写行数据。 if (Fgets(sendline, MAXLINE, fp) == NULL) return;else fputs(sendline,stdout);strcpy(cmd,sendline);//int i;//for(i=0;i<strlen();i++)//fprintf(stderr,"%d\n",(int)(sendline[i]));char * pcmd=strtok(cmd," \n");if(strcmp(pcmd,"dir")==0||strcmp(pcmd,"ls")==0){ // 将输入的字符串序列写入套接字,以便在服务器端读取 Writen(sockfd, sendline, strlen(sendline)); // 从服务器读取行数在recvline中 if (Readline2(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely"); // 将字符串转化为整数 int lines=atoi(recvline); int i;for(i=0;i<lines;i++){ Readline2(sockfd, recvline, MAXLINE); fprintf(stderr,recvline);} } else if(strcmp(pcmd,"get")==0){ Writen(sockfd, sendline, strlen(sendline)); if (Readline2(sockfd, recvline, MAXLINE) == 0)err_quit("str_cli: server terminated prematurely"); int bytes=atoi(recvline); fprintf(stderr, "file length = %d.\n",bytes); int left=bytes; FILE *fp=0; if(left>0) { char filename[100]; strcpy(filename,wdpath); strcat(filename,"/"); // strtok(NULL," \n")是get后面的文件名 strcat(filename, strtok(NULL," \n")); if((fp=fopen(filename,"wb"))==NULL) { fprintf(stderr,"failed open file %s",filename); return; } // 打印出文件路径 fprintf(stderr, "file create: %s.\n",filename); } // 从套接字中读出数据然后写入文件中 while(left>MAXLINE) { Readn(sockfd, recvline, MAXLINE); left-=MAXLINE; fwrite(recvline,MAXLINE,1,fp); } if(left<=MAXLINE) { Readn(sockfd, recvline, left); //fprintf(stderr,recvline); fwrite(recvline,left,1,fp); } fprintf(stderr, "file writen done.\n"); fclose(fp); } else if(strcmp(pcmd,"quit")==0){ fprintf(stderr, "See you!.\n"); return;}<pre name="code" class="plain"><span style="white-space:pre"></span>else if(strcmp(pcmd,"delete")==0){ Writen(sockfd,sendline,strlen(sendline)); if (Readline2(sockfd, recvline, MAXLINE) == 0){err_quit("str_cli: server terminated prematurely"); }else fprintf(stderr,"file %s delete successfully.\n",recvline); }else if(strcmp(pcmd,"put") == 0){ Writen(sockfd, sendline, strlen(sendline)); char filename[100]; strcpy(filename,wdpath); strcat(filename,"/"); strcat(filename,strtok(NULL," \n")); FILE *fp = 0; if((fp = fopen(filename,"rb")) == NULL){ fprintf(stderr,"failed open file %s",filename); return; } fprintf(stderr,"open file %s.\n",filename); long filelen; fseek(fp,0,SEEK_END); filelen = ftell(fp); rewind(fp); char flen[7]; sprintf(flen,"%d\n",(int)filelen); fprintf(stderr,"filesize %s\n",flen); Writen(sockfd,flen,strlen(flen)); fprintf(stderr,"sent filesize=%s",flen); long left = filelen; while(left > MAXLINE){ fread(sendline,MAXLINE,1,fp); Writen(sockfd,sendline,MAXLINE); left -= MAXLINE; } if(left <= MAXLINE){ fread(sendline,left,1,fp); Writen(sockfd,sendline,left); } fprintf(stderr,"file send done.\n"); fclose(fp); }else{ fprintf(stderr, "usage:cmd [args].\ncmd=\tdir\tls\tget\tquit\n"); }}}ssize_treadline(int fd, void *vptr, size_t maxlen){ssize_tn, rc;charc, *ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {again:if ( (rc = read(fd, &c, 1)) == 1) {*ptr++ = c;if (c == '\n')break;/* newline is stored, like fgets() */} else if (rc == 0) {*ptr = 0;return(n - 1);/* EOF, n - 1 bytes were read */} else {if (errno == EINTR)goto again;return(-1);/* error, errno set by read() */}}*ptr = 0;/* null terminate like fgets() */return(n);}/* end readline */ssize_tReadline2(int fd, void *ptr, size_t maxlen){ssize_tn;if ( (n = readline(fd, ptr, maxlen)) < 0)err_sys("readline error");return(n);}
执行部分截图
服务端
客户端
0 0
- linux下的简单文件服务器和客户端程序
- 简单文件服务器和客户端
- Linux socket编程-最简单的服务器和客户端程序
- Linux socket编程-最简单的服务器和客户端程序
- Ubuntu下搭建简单的nginx文件服务器
- linux下mongodb程序和c++客户端的编译
- linux下的文件服务器 FTP, NFS, Samba
- Linux c实现一个tcp文件服务器和客户端
- Linux下文件服务器配置
- Linux 文件服务器--samba的安装和配置
- 简单的客户端和服务器通信程序
- linux下简单的TCP服务端与客户端进行网络通讯的程序
- Linux下C语言程序简单的编写和调试
- Linux网络编程-简单的客户端和服务器通讯程序开发入门(2)
- Linux文件服务器的配置
- linux下搭建smba文件服务器
- linux下的ftp服务器的搭载(文件服务器2)
- Ubuntu文件服务器简单配置--samba的安装和配置
- Ubuntu14.04交叉编译U-Boot
- fedora 的学习历程
- 最大子段和的以为与二维求解
- 空心三角形(图形的输出)c语言
- 十七、代理设计模式
- linux下的简单文件服务器和客户端程序
- POJ 2942 Knights of the Round Table(点双联通+二分图+染色)
- 莫比乌斯反演模板
- SQL中 group by 1, order by 1 语句是什么意思
- Object-c------计算代码行数小demo
- Android View 事件分发机制梳理
- Qt应用程序以管理员身份启动
- 16.5.6-7学习内容
- LeetCode-46&47.Permutations