Linux基于socket 和 tcp协议的FTP文件传输系统

Linux 基于server和client的FTP文件项目

功能需求:

  • 远程操作:
    ①获取服务器的文件: get xxx
    ②显示服务器有哪些文件: ls
    ③进入服务器某文件夹: cd xxx
    ④上传文件到服务器: put xxx
    ⑤打印当前服务器路径: pwd
    ⑥退出服务器: quit
  • 本地操作:
    ①查看客户端本地文件: lls
    ②进入客户端文件夹: lcd

基本思路

  • 利用客户端和服务断框架,一旦服务端接收到客户端的连接请求,服务器fork()创建一个子进程负责处理客户端的指令,客户端发出指令,服务端根据指令做出相应的处理并返回值给客户端,客户端根据返回值打印结果。
  • 利用宏定义指令简化代码
  • 用函数封装实现小功能
  • 利用结构体储存并传递指令和返回值
  • 当客户端读取到指令输入,用cmd_handler把指令转化为宏,输送给服务端。服务端接收到指令,msg_handler进行处理,并返回data给客户端,然后客户端再用server_handler处理返回的数据,最后输出结果。

小功能实现

  • 宏定义以及结构体构建
    这里直接自定义一个头文件,包含宏定义指令和要用的结构体,以便于引用
    congfig.h:
#define LS   0
#define CD   1
#define PWD  2
#define GET  3

#define IFRET 4

#define LLS  5
#define LCD  6
#define PUT  7
#define QUIT   8
#define DOFILE 9


struct Msg                                  //传递的信息结构体
{
        int type ;                  //服务器返回类型,0表示不用返回,1表示字符串,DOFILE表示要创建文件
        char data[32];    //指令
        char secondBuf[10240];//数据,用于server和client间的通信

};
  • getdir()用空格分割处理ls的输出结果,并返回。strtok()首次调用,使用空格第一次分割,之后调用传入NULL,字符串固定用法。我们二次调用,返回值是被分割出来的第二个字符串,即文件名。
 			char *getdir(char *cmd)
           {
                  char *p;

                  p = strtok(cmd," ");
                  p = strtok(NULL," ");

                  return p;

           }
  • cd/pwd
    与ls类似,使用popen(),调用相应的命令,返回输出结果,注意cd改变目录时,调用的是chdir(),表示进入相应的文件,不能用system(),否则,是在shell窗口下进入。主窗口下没有变化。

服务端:

 case PWD:
        msg.type = 0;
        FILE *r = popen(msg.data,"r");
        fread(msg.data,sizeof(msg.data),1,r);
        write(fd,&msg,sizeof(msg));

        break;
 case CD:
        msg.type = 1;
        char *dir = getDesDir(msg.data);
        printf("dir:%s\n",dir);
        chdir(dir);
        break;
   
  • lls/lcd

同理,在客户端,直接调用相关函数:

case LLS:
        system("ls");
        break;
case LCD:
        dir = getdir(msg.data);
        chdir(dir);
        break;
  • quit
    服务端调用extit(-1)退出。

代码

  1. servser.c
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include<linux/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>
#include"config.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>



/*struct sockaddr_in {
		sa_family_t sin_family;		//协议族
		in_port_t 	sin_port;		//端口号
		struct in_addr sin_addr;		//IP地址结构体
		unsigned char	sin_zero[8];	//填充,没有实际意义,只是为更sockaddr结构在内存中相互对齐,这样才能相互转换

}*/



/*struct in_addr {
        __be32  s_addr;
};*/

int get_cmd_type( char *cmd){

        if(!strcmp("ls",cmd))           return LS;
        if(!strcmp("lls",cmd))          return LLS;
        if(!strcmp("quit",cmd))         return QUIT;
        if(!strcmp("pwd",cmd))          return PWD;

        if(strstr(cmd,"lcd")!=NULL)     return LCD;
        if(strstr(cmd,"cd")!=NULL)      return CD;
        if(strstr(cmd,"get")!=NULL)     return GET;
        if(strstr(cmd,"put")!=NULL)     return PUT;

	return -1;
}

char *getdesdir(char *cmd){

	char *p;

	p = strtok(cmd," ");
	p = strtok(NULL," ");
	
	return p;
}



void msghandler(int fd,struct Msg msg){

	int ret = get_cmd_type(msg.data);
	char *file = NULL;
	int fdfile;
	char databuf[10240] = {0};
	char *dir;
	char msg_databuf[32] = {0};

	printf("cmd:%s\n",msg.data);
	
	switch(ret){
	
		case LS:
		case PWD:
			msg.type = 1;

			FILE *r = popen(msg.data,"r");
			fread(msg.secondBuf,sizeof(msg.secondBuf),1,r);
			write(fd,&msg,sizeof(msg));

			break;
		case GET:
			strcpy(msg_databuf,msg.data);
			file = getdesdir(msg.data);
			strcpy(msg.data,msg_databuf);

			if(access(file,F_OK) == -1){//判断文件名是否存在
		
				msg.type = 1;	
				strcpy(msg.secondBuf,"file is not exist");
				write(fd,&msg,sizeof(msg));
			
			}else {
			
				msg.type = DOFILE;
	                   	
				fdfile = open(file,O_RDWR);
				read(fdfile,databuf,sizeof(databuf));
				
				close(fdfile);
				
				strcpy(msg.secondBuf,databuf);
				write(fd,&msg,sizeof(msg));
			
			}

			break;
		case PUT:
			file = getdesdir(msg.data);
                        if(access(file,F_OK) != -1){//判断文件名是否存在

				msg.type = 1;
                                strcpy(msg.secondBuf,"file is  exist");
                                write(fd,&msg,sizeof(msg));

                        }else{
				msg.type = 0;
		
                                fdfile = open(file,O_RDWR|O_CREAT,0600);
                                write(fdfile,msg.secondBuf,sizeof(msg.secondBuf));
                                close(fdfile);

				write(fd,&msg,sizeof(msg));
						
			}

			break;	
		case CD:

			dir = getdesdir(msg.data);
			
			if(access(dir,F_OK) == -1){
				msg.type = 1;
				strcpy(msg.secondBuf,"dir is not exist");	
		
			}else{
				msg.type = 0; 
				chdir(dir);
			}
			write(fd,&msg,sizeof(msg));

			break;
		case QUIT:
			msg.type = 0;
			write(fd,&msg,sizeof(msg));	
			printf("client quit\n");
			exit(-1);
			break;
		case -1:

			break;		
	}


	
}




int main(int argc ,char **argv){

	int s_fd;
	int c_fd;
	key_t key;
	pid_t pid;
	int sizec_addr;

	struct Msg msg;
	char readbuf[128] ={0}; 

	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));


	//1.socket创建套接字
			
        s_fd = socket(AF_INET,SOCK_STREAM, 0);
	if(s_fd == -1){
	
		perror("socket");
		exit(-1);
	}
	

	//2.bind(为套接字绑定地址和端口号)
	
	s_addr.sin_family = AF_INET;		//IPV4 因特网
	s_addr.sin_port   = htons( atoi(argv[2]) );//大端变小端
	inet_aton(argv[1],&s_addr.sin_addr) ;//字符串变网络形式

	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

	//3. listen()监听
	
	listen(s_fd,10 );

	printf("listen...\n");

	while(1){ //不停的接受客户端请求

	//4.accept()函数
	
		sizec_addr = sizeof(struct sockaddr_in);
		c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &sizec_addr);


		if(c_fd != -1){   //有客户端接入
		
			char *p = inet_ntoa(c_addr.sin_addr);			
			printf("client %s connect\n",p);

			pid = fork();
			if(pid == 0){
				while(1){	
					memset(msg.data,0,sizeof(msg.data));
					memset(msg.secondBuf,0,sizeof(msg.secondBuf));
					int n_read = read(c_fd,&msg,sizeof(msg));
					if(n_read == 0){
						printf("client %s out\n",p);
						break;
					
					}else if(n_read >0){
					
						msghandler(c_fd,msg);				
				
					}
				}
			}
	
		}	

	}



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


}

  1. client.c
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include<linux/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <string.h>
#include <unistd.h>
#include"config.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>




/*struct sockaddr_in {
  sa_family_t sin_family;		//协议族
  in_port_t 	sin_port;		//端口号
  struct in_addr sin_addr;		//IP地址结构体
  unsigned char	sin_zero[8];	//填充,没有实际意义,只是为更sockaddr结构在内存中相互对齐,这样才能相互转换

  }*/



/*struct in_addr {
  __be32  s_addr;
  };*/

int get_cmd_type(char *cmd){

	if(!strcmp("ls",cmd))		return LS;
	if(!strcmp("lls",cmd))          return LLS;
	if(!strcmp("quit",cmd))         return QUIT;
	if(!strcmp("pwd",cmd))          return PWD;

	if(strstr(cmd,"lcd")!=NULL)     return LCD;
	if(strstr(cmd,"cd")!=NULL)      return CD;
	if(strstr(cmd,"get")!=NULL)     return GET;
	if(strstr(cmd,"put")!=NULL)     return PUT;	


	return -1;
}

char *getdir(char *cmd){
																
	char *p;

	p = strtok(cmd," ");
	p = strtok(NULL," ");

	return p;
}



int cmd_handler(struct Msg msg,int fd){

	int ret;
	char buf[32] = {0};
	char *dir  = NULL;
	int  filefd;

	ret = get_cmd_type(msg.data);

	switch(ret){
	
		case LS:   
		case CD:	
		case PWD:
		case GET:
			write(fd,&msg,sizeof(msg));
			break;	
		case PUT:
			strcpy(buf,msg.data);
			dir = getdir(buf);
			
			if(access(dir,F_OK) == -1){
			
				printf("%s is not exist\n",dir);
			}else{
			
				filefd = open(dir,O_RDWR);
				read(filefd,msg.secondBuf,sizeof(msg.secondBuf));
				close(filefd);
				
				write(fd,&msg,sizeof(msg));				
			
			}			
	
			break;
		case LLS:
			write(fd,&msg,sizeof(msg));
			system("ls");
			break;
		case LCD:
			write(fd,&msg,sizeof(msg));
			dir = getdir(msg.data);
			
			if(access(dir,F_OK) == -1){
				printf("dir is not exist\n");	
		
			}else{
				chdir(dir);
			}	
			break;
		case QUIT:
			strcpy(msg.secondBuf,"quit");
			printf("client quit\n");
			write(fd,&msg,sizeof(msg));
			close(fd);
			exit(-1);
			break; 	
	
	
	}


	return ret;
}

void server_message_handler(struct Msg msg,int fd){

	int n_read;
	int newfilefd;
	char *file = NULL;

	memset(msg.data,0,sizeof(msg.data));	
	n_read = read(fd,&msg,sizeof(msg));
	
	if(n_read == 0){
		printf("server is out,quit\n");
		exit(-1);
	}
	
	if(msg.type == DOFILE){

		file = getdir(msg.data);
		newfilefd = open(file,O_RDWR|O_CREAT,0600);
		if(newfilefd == -1){
					
			perror("newfilefd");	
		}
		write(newfilefd,msg.secondBuf,strlen(msg.secondBuf));
		close(newfilefd );

	}else if(msg.type == 1){
		printf("................................\n");
		printf("\n%s\n",msg.secondBuf);
		printf("................................\n");

		fflush(stdout);	
	}else if(msg.type == 0){
	
		return ;	
	
	}
	
}




int main(int argc ,char **argv){

	int c_fd;//socket的返回值
	int c_ret;//connect的返回值
	int ret_cmd_handler;//cmd_handler()的返回值
	int sizec_addr;
	struct Msg msg;

	struct sockaddr_in c_addr;
	memset(&c_addr,0,sizeof(struct sockaddr_in));


	//1.socket创建套接字

	c_fd = socket(AF_INET,SOCK_STREAM, 0);
	if(c_fd == -1){

		perror("socket");
		exit(-1);
	}


	//2.connect(向服务器发送连接请求)

	c_addr.sin_family = AF_INET;		//IPV4 因特网
	c_addr.sin_port   = htons( atoi(argv[2]) );//大端变小端
	inet_aton(argv[1],&c_addr.sin_addr) ;//字符串变网络形式

	c_ret = connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in));
	if(c_ret == -1){
		printf("connect fail\n");
		exit(-1);
	}
	printf("connect %s\n",argv[1]);//显示当前连接服务端ip地址

	while(1){ //不停的获取用户输入

		memset(msg.data,0,sizeof(msg.data));//每次用户输入前,先把数据初始化
		memset(msg.secondBuf,0,sizeof(msg.secondBuf));
		msg.type = 0;
		putchar('>');//每次输入前,打印‘>’,等待输入 
		fflush(stdout);	

		gets(msg.data);//获取输入命令,cmd,存入mag.data中

		if(strlen(msg.data) == 0 ){//当输入为空
		
			continue;	//直接进入下一次循环,重新获取输入
		
		}else{
		
			ret_cmd_handler = cmd_handler(msg,c_fd);//根据输命令入,发送不同的数据给服务端
			
			if(ret_cmd_handler > IFRET){ //IFRET为宏,大于它,表示server返回client的结果不需要处理
			
				fflush(stdout);//冲刷缓冲区,是数据即时输出
				continue;//进入下一次循环	

			}
			
			if(ret_cmd_handler == -1){//非法指令
				
				printf("command not found\n");
				fflush(stdout);//冲刷缓冲区,是数据即时输出
				continue;//进入下一次循环

			}else{

				server_message_handler(msg,c_fd);//服务端返回信息处理
			
			}
		
		}

	}


	return 0;


}



  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

elaot

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值