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