⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 myftp_serv.c

📁 Linux下的ftp服务器
💻 C
📖 第 1 页 / 共 2 页
字号:
#include "myftp_cmd_string.h"
#include "myftp_state_machine.h"
#include "mystr.h"
const char* myftp_pid_file="myftp_pid.pid";//存放服务器主进程的pid文件

enum EVmyftpcmdflags
{
	start_cmd = 1,
	stop_cmd,
	restart_cmd,
	undefine_cmd
};

void init_ftp_daemon();
enum EVmyftpcmdflags myftp_serv_cmd_trans(char* argv);
enum EVmyftpcmdflags myftp_chk_cmd(int argc, char* argv);
int myftp_read_pidfile(char* pid_str);
int myftp_call_popen(char* pid_str,char* pid_val);
int myftp_if_running();
int myftp_is_pidfile_exist();
void myftp_stop();
void myftp_write_pid_to_file();
void myftp_creat_pidfile();
void myftp_start();
void myftp_arg_handler(enum EVmyftpcmdflags cmd_enum);
void myftp_getpeername(int clientsock_fd, pMYFTP_SESSION p_thesession);

/*======================================================================
function:父进程收尸函数
======================================================================*/
void sig_handle() 
{
	wait(0);
}

/*======================================================================
function:申请会话空间
======================================================================*/
void myftp_session_malloc(pMYFTP_SESSION* p_thesession)
{
	*p_thesession=(pMYFTP_SESSION)malloc(sizeof(MYFTP_SESSION));
	if (!(*p_thesession))
	{
		die("malloc session error!\n");
	}
}

/*======================================================================
function:初始化会话空间
======================================================================*/
pMYFTP_SESSION myftp_make_session()
{
	pMYFTP_SESSION p_thesession=NULL;

	myftp_session_malloc(&p_thesession);

	INIT_SESSION(p_thesession);

	return p_thesession;
}

/*======================================================================
function:创建服务器监听套接字
======================================================================*/
int myftp_make_listen_socket(pMYFTP_SESSION p_thesession)
{
	int listen_fd;
	int yes;
	MYFTP_SOCKADDR servaddr;

	servaddr.un.u_sockaddr_in.sin_family=AF_INET;
	servaddr.un.u_sockaddr_in.sin_addr.s_addr=htonl(INADDR_ANY);
	servaddr.un.u_sockaddr_in.sin_port=htons(myftp_conf.listen_port);
	
	listen_fd=socket(AF_INET,SOCK_STREAM,0);//监听套节字
	if (-1==listen_fd)
	{
		die("socket error.\n");
	}
	yes=1;
	if (setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes))<0)
	{
		die("setsockopt error\n");
	}
	if (-1==bind(listen_fd,&servaddr.un.u_sockaddr,sizeof(servaddr)))
	{
		die("bind error.\n");
	}
	
	if (-1==listen(listen_fd,LISTEN_BACKLOG))
	{
		die("listen error.\n");
	}

	p_thesession->cntl_listen_fd=listen_fd;//将服务器监听套接字存放起来
	return listen_fd;
}

/*======================================================================
function:分配存放客户端命令字符串空间,并初始化
======================================================================*/
pMYSTR myftp_make_cmd_str_buf()
{
	pMYSTR pcmdline=NULL;

	myftp_alloc_mystr(&pcmdline);

	INIT_MYSTR(pcmdline);

	return pcmdline;
}

/*======================================================================
function:服务器监听超时函数
注:超时值为零,即永远等待,直到有客户连接。
======================================================================*/
void myftp_select_socket(pMYFTP_SESSION p_thesession)
{
	int select_result;
	FD_ZERO(&p_thesession->read_fd);
	FD_SET(p_thesession->cntl_listen_fd,&p_thesession->read_fd);
	do 
	{
		select_result=select(p_thesession->cntl_listen_fd+1,&p_thesession->read_fd,0,0,NULL);

	} while (select_result < 0 && errno == EINTR);
}

/*======================================================================
function:超时功能函数
======================================================================*/
int myftp_timeout(pMYFTP_SESSION p_thesession)
{
	fd_set data_fd_set;
	struct timeval timeout;
	FD_ZERO(&data_fd_set);
	FD_SET(p_thesession->cmd_cntl_fd, &data_fd_set);
	int select_rslt=-1;
	while (1)
	{				
		timeout.tv_sec=myftp_conf.timeout_val;
		timeout.tv_usec=0;
		
		if (!(select_rslt=select(p_thesession->cmd_cntl_fd+1,&data_fd_set,0,0,&timeout)))//超时退出
		{
			return TRUE;
		}
		else if (select_rslt <0 && errno==EINTR)
		{
			continue;
		}
		else
		{
			return FALSE;
		}
	}
}

/*======================================================================
function:ftp服务器主函数
======================================================================*/
void myftp_main_server()
{
	int client_addrlen=sizeof(MYFTP_SOCKADDR);
	
	pMYFTP_SESSION p_thesession=myftp_make_session();//创建并初始化会话
	pMYSTR pcmdline=myftp_make_cmd_str_buf();//分配命令串的存放空间
	int listen_fd=myftp_make_listen_socket(p_thesession);//创建监听套接字
	p_thesession->p_cmd_line=pcmdline;
	signal(SIGCHLD,sig_handle);

	FD_ZERO(&p_thesession->read_fd);
	FD_SET(p_thesession->cntl_listen_fd,&p_thesession->read_fd);
	while (1)
	{		
		int client_cntl_sockfd;
		int fd;
		myftp_select_socket(p_thesession);
		client_cntl_sockfd=accept(listen_fd,0,&client_addrlen);//控制连接
		if(-1==client_cntl_sockfd)
		{
			die("accept error.\n");
		}
		write(client_cntl_sockfd,"220 (zjw_mini ftp 0.1)\r\n",strlen("220 (zjw_mini ftp 0.1)\r\n"));
		myftp_getpeername(client_cntl_sockfd,p_thesession);	

		int child_serv=fork();
		if (!child_serv)
		{
			close(listen_fd);
			close(p_thesession->cntl_listen_fd);
			int is_child_quit = -1,client_count;
			
			while (1)
			{
				if (myftp_timeout(p_thesession))//如果超时
				{
					write(p_thesession->cmd_cntl_fd,"421 Timeout.\r\n",strlen("421 Timeout.\r\n"));
					break;
				}
				client_count=read(p_thesession->cmd_cntl_fd,pcmdline->cmd_line,sizeof(pcmdline->cmd_line));
				if (client_count < 0 && errno == EINTR)
					//之前没有这个判断,会导致当read被子进程\
					中断后,读出来的值为空值,从而导致向客户端发送错误信息。并且导致二次传输数据时,\
					连接断开。
				{
					continue;
				}
				if (!myftp_splite_cmd_line(pcmdline))//分割命令字符串
					continue;

				is_child_quit=myftp_state_machine_entry(p_thesession);//状态机入口函数
				
				memset(p_thesession->p_cmd_line,0,sizeof(MYSTR));
				if (is_child_quit == CHILD_QUIT)//用户执行QUIT命令后
					break;
				else if(is_child_quit==client_cntl_sockfd)//用户执行REIN命令后
				{
					memset(p_thesession->user_str, 0, USER_LEN);
					memset(p_thesession->passwd_str, 0, PASS_LEN);
				}
			}
			exit(0);
		}
		close(client_cntl_sockfd);				
	}
}

/*======================================================================
function:获取客户端ip地址
======================================================================*/
void myftp_getpeername(int clientsock_fd, pMYFTP_SESSION p_thesession)
{
	int len=sizeof(MYFTP_SOCKADDR);
	int retval=getpeername(clientsock_fd,&p_thesession->remote_addr.un.u_sockaddr,&len);
	if (retval)
	{
#ifdef __MYDEBUG
		die("getpeername error\n");
#endif
	}
	strcpy(p_thesession->remote_data_ip_str,\
		(char*)inet_ntoa(p_thesession->remote_addr.un.u_sockaddr_in.sin_addr));
	p_thesession->cmd_cntl_fd=clientsock_fd;//将控制连接套接字保存起来
}

/*======================================================================
function:初始化服务器守护进程
=======================================================================*/
void init_ftp_daemon()
{
	pid_t ftp_pid;
	ftp_pid=fork();
	int i;
	if (-1==ftp_pid)
	{
		die("fork daemon error.\n");
	}
	else if (0<ftp_pid)
	{
		exit(0);
	}
	else if (0==ftp_pid)
	{
		setsid();
		ftp_pid=fork();
		if (-1==ftp_pid)
		{
			die("fork2 daemon error.\n");
		}
		else if (0<ftp_pid)
		{
			exit(0);
		}
		else if(0==ftp_pid)
		{
			if(!myftp_is_pidfile_exist())
			{
				myftp_creat_pidfile();
			}
			myftp_write_pid_to_file();
			for(i=3;i < NOFILE;++i)//关闭打开的文件描述符 
				close(i); 
			chdir("/");//改变工作目录到/ 
			
		}
	}
}

/*==================================================================
function:解析用户输入的参数
===================================================================*/
enum EVmyftpcmdflags myftp_serv_cmd_trans(char* argv)
{
	enum EVmyftpcmdflags cmd_enum;

	if (strcmp(argv,cmd_string_start) == 0)
	{
		cmd_enum = start_cmd;
		goto BACK;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -