Linux Socket套接字实现简易的FTP服务器

本文档详细描述了一个基于TCP的服务器程序,它解析客户端发送的命令,支持ls,pwd,cd,get,put等操作,通过popen和chdir进行目录管理和文件交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

服务器

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

char * getDesDir(char *str)
{
	char *token;
	token = strtok(str," ");
	token = strtok(NULL," ");
	int i=0;//去除fgets函数输入后带的'\n'字符,将'\n'用'\0'替换
	//否则后面的函数执行的语句后面会带一个'\n',导致不能改变目录。
	while(token[i]!='\n'){
		i++;
	}	
	token[i]='\0';
	return token;
}

int get_cmd_type(struct Msg msg)
{
	if(!strcmp("ls\n",msg.cmd))         return LS;//这后面加'\n'是因为客户端输入命令用的是fgets函数,会带一个'\n',这里不加的话会比较不出来。
	if(!strcmp("quit\n",msg.cmd))       return QUIT;
	if(!strcmp("pwd\n",msg.cmd))        return PWD;
	if(!strcmp("cd\n",msg.cmd))	    return 100;//为了避免客户端单独输入cd导致运行getDesDir函数时出错,且单纯一个字符串"cd"对chdir函数来说本来就是不能执行的
	if(strstr(msg.cmd,"cd") != NULL)  return CD;
	if(strstr(msg.cmd,"get") != NULL) return GET;
	if(strstr(msg.cmd,"put") != NULL) return PUT; 
 
	return 100;		 
}
void msg_handler(struct Msg msg,int fd)
{
	int ret;
	int filefd;
	FILE *fp;
	char *dir=NULL;
	char *file_name=NULL;
	struct Msg readmsg;
	memset(&readmsg,'\0',sizeof(readmsg));
	ret=get_cmd_type(msg);
	printf("client:%s\n",msg.cmd);
	switch(ret){
		case LS:
		case PWD:
			fp=popen(msg.cmd,"r");//这里使用popen函数是应为popen可以把执行结果存到文件里面,可以发送给客户端system和execl函数做不到
			fread(msg.cmd,sizeof(msg.cmd),1,fp);
			write(fd,&msg,sizeof(msg));
			fclose(fp);
			break;
		case CD:
			dir=getDesDir(msg.cmd);
			chdir(dir);//这里不能用system和popen,这两个函数不能改变路径
			break;
		case GET:
			strcpy(readmsg.cmd,msg.cmd);
			file_name=getDesDir(msg.cmd);
			if(access(file_name,F_OK)==-1){//判断文件是否存在
				 strcpy(msg.cmd,"No find this file!");
				 write(fd,&msg,sizeof(msg));
			}else{
				filefd=open(file_name,O_RDWR);
				read(filefd,readmsg.file_content,sizeof(readmsg.file_content));
				write(fd,&readmsg,sizeof(readmsg));
				close(filefd);
			}
                        break;
		case PUT:
			filefd=open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0600);
			write(filefd,msg.file_content,strlen(msg.file_content));
			close(filefd);
			break;
		case QUIT:
			printf("client quit\n");
			exit(-1);
	}
}
int main(int argc,char **argv)
{
	if(argc!=3){
		printf("argc");
	}
	int s_fd;
	int c_fd;
	int n_read;
	struct Msg msg;
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;
	int size=sizeof(c_addr);
	memset(&s_addr,0,sizeof(s_addr));
	memset(&c_addr,0,sizeof(c_addr));

	s_addr.sin_family=AF_INET;
	s_addr.sin_port=htons(atoi(argv[2]));//host to net把为主机字节序的端口号转换成网络字节序(大端字节序)
	inet_aton(argv[1],&s_addr.sin_addr);//把IP地址的ascll转换成网络字节序

	//socket
	if((s_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
		perror("socket");
	}
	//bind
	if(bind(s_fd,(struct sockaddr *)&s_addr,sizeof( struct sockaddr_in))==-1){
		perror("bind");
	}
	listen(s_fd,10);
	//accept
	while(1){
	//accept
		int c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&size);
		printf("clicent success!\n");
		if(fork()==0){
			while(1){
				memset(&msg,0,sizeof(msg));
                                n_read=read(c_fd,&msg,sizeof(msg));
                                if(n_read==0){
                                        printf("client out!\n");
                                        break;
                                }else if(n_read>0){
                                        msg_handler(msg,c_fd);
                                }
			}
		}	
		
	}
	close(s_fd);
	close(c_fd);
	return 0;
}

客服端

#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include<string.h>
#include <unistd.h>
#include<string.h>
#include "config.h"
#include <sys/stat.h>
#include <fcntl.h>
char * getDesDir(char *str)
{
        char *token;
        token = strtok(str," ");
        token = strtok(NULL," ");
        int i=0;//去除fgets函数输入后带的'\n'字符,将'\n'用'\0'替换
        //否则后面的函数执行的语句后面会带一个'\n',导致不能改变目录。
        while(token[i]!='\n'){
                i++;
        }       
        token[i]='\0';
        return token;
}
void server_msg_handler(struct Msg msg)
{
	int filefd;
	char *file_name=NULL;
	if(strstr(msg.cmd,"get") != NULL){
		filefd=open(getDesDir(msg.cmd),O_RDWR|O_CREAT,0600);
                write(filefd,msg.file_content,strlen(msg.file_content));
                close(filefd);	
	}
	else{
		printf("%s\n",msg.cmd);
	}
}
int get_cmd_type(struct Msg msg)
{
	if(!strcmp("ls\n",msg.cmd))         return LS;//这后面加'\n'是因为客户端输入
//命令用的是fgets函数,会带一个'\n',这里不加的话会比较不出来。
        if(!strcmp("quit\n",msg.cmd))       return QUIT;
        if(!strcmp("pwd\n",msg.cmd))        return PWD;
        if(!strcmp("cd\n",msg.cmd))         return 100;//为了避免客户端单独输入cd导>致运行getDesDir函数时出错,且单纯一个字符串"cd"对chdir函数来说本来就是不能执行的
        if(strstr(msg.cmd,"cd") != NULL&strstr(msg.cmd,"lcd")==NULL)
					  return CD;
	//确保cd xx和lcd xx命令不会冲突
        if(strstr(msg.cmd,"get") != NULL) return GET;
        if(strstr(msg.cmd,"put") != NULL) return PUT;

	if(!strcmp("lls\n",msg.cmd))	  return LLS;
	if(strstr(msg.cmd,"lcd")!=NULL)	  return LCD;
	if(!strcmp("lpwd\n",msg.cmd))	  return LPWD;
        return 100;

}
void cmd_handler(int c_fd,struct Msg msg)
{
	int ret;
	int filefd;
	char *file_name=NULL;
	char *dir=NULL;
	struct Msg readmsg;
	memset(&readmsg,'\0',sizeof(readmsg));
	ret=get_cmd_type(msg);
	switch(ret){
		case LS:
		case PWD:
		case CD:
		case GET:
			write(c_fd,&msg,sizeof(msg));
			break;
		case PUT:
			strcpy(readmsg.cmd,msg.cmd);//执行了下面的函数以后cmd命令就会只剩一个put了
						    //为了确保命令能发到服务端去,这里新建了一个结
					            //构体,把命令和要上传的文件的内容存在cmd和file_content里。
			file_name=getDesDir(msg.cmd);
                        if(access(file_name,F_OK)==-1){//判断文件是否存在
                               	printf("No this file!\n");
                        }else{
                                filefd=open(file_name,O_RDWR);
                        }
				        if(filefd==-1){
					        perror("open defeat!");
				        }
                        else{
                          read(filefd,readmsg.file_content,sizeof(readmsg.file_content));
                          write(c_fd,&readmsg,sizeof(readmsg));
                          close(filefd);
                        }
                        break;	
		case LPWD:
			system("pwd");
			break;
		case LLS:
			system("ls");
			break;
		case LCD:
			dir=getDesDir(msg.cmd);
                        int i=chdir(dir);
			if(i==-1){
				printf("chdir defeat!\n");
			}
                        break;
		case QUIT:
			write(c_fd,&msg,sizeof(msg));
			printf("client quit!\n");
			close(c_fd);
			exit(-1);
	}

}
int main(int argc,char **argv)
{
	if(argc!=3){
		printf("argc");
	}
	int c_fd;
	int n_read;
	struct Msg msg;
	struct sockaddr_in c_addr;
	int size=sizeof(c_addr);
	memset(&c_addr,0,sizeof(c_addr));

	c_addr.sin_family=AF_INET;
	c_addr.sin_port=htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);	

	//int socket(int domain, int type, int protocol);
	if((c_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
		perror("socket");
	}
	//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	int con_err=connect(c_fd,( struct sockaddr *)&c_addr,size);
	if(con_err==-1){
		perror("connnect");
		exit(-1);
	}
	printf("connect success>\n");
	while(1){
		if(fork()==0){
			while(1){
				memset(&msg,0,sizeof(msg));
                                n_read=read(c_fd,&msg,sizeof(msg));
                                if(n_read==0){
                                        printf("server out!\n");
                                        break;
                                }else if(n_read>0){
                                	server_msg_handler(msg);
				}
			}
		}
		else{
			while(1){
				memset(&msg,0,sizeof(msg));
				fgets(msg.cmd,128,stdin);
				cmd_handler(c_fd,msg);/*cmd_handler函数用来区分是本地命令(lcd  lpwd lls等)
还是服务器指令(ls cd pwd get put qiut等),本地指令直接调用system,
不需要发给服务器,服务器指令需要发给服务器。*/
			}
		}
		break;
	}
	close(c_fd);
	return 0;
}

头文件

#define LS   0
#define GET  1
#define PWD  2
#define CD   3
#define LCD  5
#define LLS  6
#define PUT  7
#define LPWD 8
#define QUIT 9

struct Msg
{
        char cmd[1024];//存放客户端指令
        char file_content[1024];//存放文件的内容,get xx文件和put xx文件时需要把文件内容放到这里面
};
        

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值