基于TCP协议的简易FTP云盘

        这个项目分成ftp客户端及服务端,实现的功能和Linux开源的ftp服务器类似,客戶端通过网络,远程获取服务端磁盘上的文件夹内容,下载文件,上传文件等功能。(基本功能描述)

        ftp服务器用到的是Socket通信,当收到客户端接入的时候,创建子进程对接连接,子进程启动后分析来自客户端的指令。比如收到get file1的指令,是客户端想要获取file1文件的,先用strstr函数进行字符串分割,获取到文件名,在判断文件是否存在,如果文件存在,就读取文件內容,再将內容通过套接字发给客户端,客户端收到数据后,创建文件,并将收到的数据写入文件,完成文件的远程下载。

        上传文件和下载文件类似,主要还是涉及文件的操作,字符串的操作,以及网络编程。

        还支持了Is、pwd、cd等Linux系统常用的指令。普通指令的实现用popen来调用系统质量,并读取执行的结构。如果不需要获取执行结果,用system函数调用就可以了。

一、实现的功能

利用socket,建立起服务端与客户端的对接;(服务端能支持多台客户端的同时连接)

客户端

ls:查看服务端当前路径下的所有文件;
lls:查看客户端当前路径下的所有文件;
cd xx:服务端进入xx路径;
lcd xx:客户端进入xx路径;
pwd:查看服务端当前路径;
lpwd:查看客户端当前路径;
get xx:从服务端当前路径获取xx文件到客户端当前路径上;
put xx:将客户端当前路径xx文件发送到服务端当前路径;
quit:断开客户端连接;

服务端

1、不断监听客户端的指令(等待指令)。
2、在接收上面客户端的指令后,去执行指令。
 

二、总体代码

服务端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include "msg.h"

int get_cmd_type(char *cmd)    //将从客户端发送过来的指令转换成整数返回
{
	if(!strcmp("ls",cmd))		return LS;
	if(!strcmp("quit",cmd))		return QUIT;
	if(!strcmp("pwd",cmd))		return PWD;
	
	if(strstr(cmd,"cd") != NULL)	return CD;
	if(strstr(cmd,"get") != NULL)	return GET;
	if(strstr(cmd,"put") != NULL)	return PUT;

	return 11;
}

char *getdir(char *cmsg)      //将需要带参数的指令进行指令参数分离,返回分离出的参数
{
	char *p;
	p=strtok(cmsg," ");   //以空格分离
	p=strtok(NULL," ");
	return p;
}

void msg_handle(struct Msg msg,int fd)  //对客户端发送的消息进行相对应处理
{
	char dataBuf[1024] = {0};     	//用来存放GET的文件数据
	char *file = NULL;       	//用来放GET的文件名
	int fdfile;         		//GET时open的fd
	char *dir;    			//存放cd函数进入地址,即文件夹名字
    
	printf("cmd:%s\n",msg.cmd);     //打印客户端发来消息里的指令名
	int ret = get_cmd_type(msg.cmd);//获取指令所对应的整数
	
	switch(ret)
	{
		case LS:
		case PWD:
			FILE *r = popen(msg.cmd,"r");       //使用popen调用msg.cmd里的ls或pwd指令,并将运行结果读入到文件流r
			fread(msg.cmd,sizeof(msg.cmd),1,r); //将文件流r里的数据读入到msg.cmd
			write(fd,&msg,sizeof(msg));         //将msg里的数据通过fd发送给客户端
			break;
		case CD:
			dir = getdir(msg.cmd);    //将提取cd后面的参数,即文件夹名字,赋给dir
			printf("dir:%s\n",dir);	  //服务端打印要进入的文件夹名字
			chdir(dir);         	  //使用chdir改变服务器当前路径,进入到dir文件夹,不使用system
			break;
		case GET:      			  //从服务端获取文件
			file = getdir(msg.cmd);   //分离get后面的参数,即文件名,赋给file
			if(access(file,F_OK) == -1){   	        //使用access函数判断该文件是否存在,不存在返回值为-1
				strcpy(msg.cmd,"no the file!"); //复制该字符串到msg.cmd
				write(fd,&msg,sizeof(msg));     //将该字符串发送给客户端
			}else{
				fdfile = open(file,O_RDWR);     //将file文件以可读可写方式打开
				read(fdfile,dataBuf,sizeof(dataBuf));   //将fdfile中的数据读入到dataBuf中
				close(fdfile);                  //关闭文件
				strcpy(msg.cmd,dataBuf);        //将dataBuf中的数据复制到msg.cmd中
				write(fd,&msg,sizeof(msg));     //将msg里的数据通过fd发送给客户端
			}
			break;
		case PUT:      //将服务端文件发送给客户端
			fdfile = open(getdir(msg.cmd),O_RDWR|O_CREAT,0666);  //将客户端发送的文件在服务端打开,如果没有就创建该文件
			write(fdfile,msg.buf,strlen(msg.buf));   	     // 将msg.buf里的数据写入到fdfile所指的文件里
			close(fdfile);                                       // 关闭文件
			break;
		case QUIT:
			printf("client quit!\n");
			exit(-1);
	}
}

int main(int argc,char **argv)
{		
	int c_fd;  
	int s_fd;  
	char readBuf[128];
	int n_read;

	struct sockaddr_in s_addr;   //该结构体用来处理网络通信的地址族、端口号、IP地址等。s_addr服务端
	struct sockaddr_in c_addr;   //客户端
   	struct Msg msg;    	     //一个包含种类type,指令cmd,缓冲secondBuf的结构体
	memset(&s_addr,0,sizeof(struct sockaddr_in));  //初始化s_addr
	memset(&c_addr,0,sizeof(struct sockaddr_in));
	
	if(argc != 3)    //判断参数个数是否正确,不正确则退出程序
	{
		printf("Parameter error: ip prot \n");
		exit(-1);
	}
	//1.socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);   //创建socket,设置TCP协议
	
	if(s_fd == -1)
	{
		perror("socket:");
		exit(-1);
	}
	
	//2.bind
	s_addr.sin_family = AF_INET;     
	s_addr.sin_port = htons(atoi(argv[2]));   //argv[2]为端口号,需要通过htons函数将主机字节序转网络字节序
	inet_aton(argv[1],&s_addr.sin_addr);    //argv[1]为IP地址,需要通过inet_aton将argv[1]字符串转网络字节序
	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));   //绑定设置数据
	//3.listen
	listen(s_fd,10);   //监听,最多10个
	//4.accept
	int len = sizeof(struct sockaddr_in);
	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&len);   //接受客户端连接
		if(c_fd == -1){  //判断是否连接失败,方便查错
			perror("accept:");
		}
		printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));   //连接成功后服务端打印接入的客户端IP地址

		if(fork() == 0){	//创建子进程执行客户端所发信息,父进程继续监听,每有一个客户端接入,则创建一个子进程为其服务
	//5.read
			while(1){
				memset(msg.cmd,0,sizeof(msg.cmd));      //初始化msg.cmd
				n_read = read(c_fd,&msg,sizeof(msg));     //读取客户端通过c_fd发送过来的消息,没有就阻塞在这,知道消息发送过来
				if(n_read == -1){				//如果n_read==-1,则read失败,方便排错
					perror("read:");
				}else if(n_read == 0){           
					printf("client out\n");
					break;
				}else{
					msg_handle(msg,c_fd);       	//对客户端发送的消息进行处理
				}
			}	
		}
	}

	close(c_fd);
	close(s_fd);
	
	return 0;
}

客户端

#include <stdio.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "msg.h"

int get_cmd_type(char *cmd)    //将命令字符串转换成整数
{
	if(!strcmp("ls",cmd))	 	return LS;
	if(!strcmp("quit",cmd)) 	return QUIT;
	if(!strcmp("lpwd",cmd))		return LPWD;
	if(!strcmp("pwd",cmd))		return PWD;
	if(!strcmp("lls",cmd))		return LLS;
	if(strstr(cmd,"lcd"))		return LCD;     //LCD必须写在CD前,否则当输入lcd时将会返回CD
	if(strstr(cmd,"cd"))		return CD;
	if(strstr(cmd,"get"))		return GET;
	if(strstr(cmd,"put"))		return PUT;

	return -1;
}

char *getdir(char *cmd)      //分离参数
{
	char *p;
	p = strtok(cmd," ");      //strtok函数会破坏被分解字符串的完整,调用前和调用后的cmd已经不一样了。
	p = strtok(NULL," ");
	return p;
}

int cmd_handle(struct Msg msg,int fd)    //命令处理
{
	char buf[32];   //put时用来存放msg.cmd
	int ret;
	int filefd;
	char *dir = NULL;
	
	ret = get_cmd_type(msg.cmd);      //判断输入的是哪个命令
	switch(ret)
	{
		case LS:
		case CD:
		case PWD:                    //当输入命令是ls、cd、pwd等只对服务器操作的指令时,将msg里的所有数据发送给服务器
			write(fd,&msg,sizeof(msg));
			break;
		case GET:                  //当为get时,将msg里数据发给服务器
			write(fd,&msg,sizeof(msg));
			break;
		case PUT:
			strcpy(buf,msg.cmd);     //当为put时,将msg.cmd复制给buf
			dir = getdir(buf);         //分离参数,即所需发送的文件名,buf破坏
			if(access(dir,F_OK) == -1){     //判断客户端是否存在该文件
				printf("%s not exsit\n",dir);
			}else{
				filefd = open(dir,O_RDWR);     //如果有就打开该文件
				read(filefd,msg.buf,sizeof(msg.buf));    //将文件里的数据读入到msg.buf
				close(filefd);                 //关闭文件
				write(fd,&msg,sizeof(msg));      //将msg里的数据发送给服务器
			}
			break;
		case LLS:            		//当为lls指令时,在客户端调用ls即可
			system("ls");    	//system函数调用完ls后,将自动打印输出结果
			break;
		case LPWD:            		//当为lpwd指令时,在客户端调用pwd即可
			system("pwd");    	//system函数调用完pwd后,将自动打印输出结果
			break;
		case LCD:           		//当为lcd指令时,分离得出路径名,调用chdir函数改变客户端当前路径
			dir = getdir(msg.cmd);
			chdir(dir);
			break;
		case QUIT:       		//当为quit指令时,将消息发送给服务器,然后关闭fd,结束客户端
			strcpy(msg.cmd,"quit");
			write(fd,&msg,sizeof(msg));
			close(fd);
			exit(-1);
	}
	return ret;
}

void handle_server_msg(int c_fd,struct Msg msg)   //当接受到ls,get、pwd等指令时,客户端处理服务器发送来的消息
{
	int n_read;
	struct Msg msgget;
	int newfilefd;
    
	n_read = read(c_fd,&msgget,sizeof(msgget));   //将服务器发送来的信息读入msgget
	
	if(n_read == 0){
	
		printf("server is out,quit\n");
		exit(-1);
	}else if(strstr(msg.cmd,"get")){         	//当指令为get时执行
		char *p = getdir(msg.cmd);            	//提取想要从服务器端获取的文件名
		newfilefd = open(p,O_RDWR|O_CREAT,0600);  //在客户端打开该文件,没有就创建
		write(newfilefd,msgget.cmd,strlen(msgget.cmd));   //将msgget里的数据写入该文件
		close(newfilefd);
		putchar('>');
		fflush(stdout);
	}else{
		printf("-------------------------------\n");      //当指令为ls或pwd时,打印msgget.cmd里的数据
		printf("\n%s\n",msgget.cmd);
		printf("-------------------------------\n");
		putchar('>');
		fflush(stdout);
	}
}


int main(int argc,char **argv)
{		
	int c_fd;
	
	struct sockaddr_in c_addr;     
	struct Msg msg;
	memset(&c_addr,0,sizeof(struct sockaddr_in));

	if(argc!=3)
	{
		printf("Parameter error: ip prot\n");
		exit(-1);
	}
	
	//1.socket
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1){
		perror("socket:");
		exit(-1);
	}
	
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);
	
	//2.connect
	if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr))==-1){//connect函数连接服务器,连接失败则返回-1
		perror("connect:");
		exit(-1);
	}
	printf("connect...\n");                 //连接成功打印connect...
	int mark = 0;
	
	while(1){
		memset(msg.cmd,0,sizeof(msg.cmd));   //初始化msg.cmd
		if(mark == 0)                      
		printf(">");                      //使界面好看
		gets(msg.cmd);                   //从按键获取msg.cmd,即输入指令
		
		if(strlen(msg.cmd) == 0){           //如果输入的指令长为0,判断是否打印>,continue后下面语句不执行,从while循环第一句重新开始
			if(mark == 1)
			printf(">");
			continue;
		}
		mark = 1;
		
		int ret = cmd_handle(msg,c_fd);   //命令处理函数
		if(ret > IFGO){       //IFGO的值为3,即指令为lcd、lls、cd、put、quit、lpwd时,打印>,然后重新循环,下面语句不执行
			putchar('>');
			fflush(stdout);
			continue;
		}
		if(ret == -1){              //如果命令处理函数返回的值为-1,则说明该命令不存在
			printf("cmd no have!\n");
			printf(">");
			fflush(stdout);
			continue;
		}
		handle_server_msg(c_fd,msg);    //当接受到ls,get、pwd等指令时,客户端处理服务器发送来的消息
	}
	return 0;
}

msg.h

#define LS 0
#define GET 1
#define PWD 2

#define IFGO 3

#define LCD 4
#define LLS 5
#define CD 6
#define PUT 7

#define QUIT 8
#define LPWD 9


struct Msg
{
        int type;
        char cmd[1024];
        char buf[128];

};

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值