linux下的简单文件服务器和客户端程序

本文是我的一次作业,由于花了很多精力,记下来以后可能还会用到。代码部分是从老师那拷贝的,作业是实现代码中没有实现的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)
{
	int					listenfd, connfd;
	pid_t				childpid;
    
    // 套接字地址结构的长度,一般为unit32_t
	socklen_t			clilen;
    
    // 网际套接字地址结构
	struct sockaddr_in	cliaddr, servaddr;
	void				sig_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() */
			else
				err_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_t		n;
	char		buf[MAXLINE];
	char		sendline[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_t	pid;
	int		stat;

	while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
		printf("child %d terminated\n", pid);
	return;
}

ssize_t
readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t	n, rc;
	char	c, *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_t
Readline2(int fd, void *ptr, size_t maxlen)
{
	ssize_t		n;

	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)
{
	int					sockfd;
    
    // 网际套接字地址结构
	struct sockaddr_in	servaddr;
    
	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_t
readline(int fd, void *vptr, size_t maxlen)
{
	ssize_t	n, rc;
	char	c, *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_t
Readline2(int fd, void *ptr, size_t maxlen)
{
	ssize_t		n;

	if ( (n = readline(fd, ptr, maxlen)) < 0)
		err_sys("readline error");
	return(n);
}

 执行部分截图 

服务端



客户端





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值