📄 myftp_state_machine.c
字号:
{
return myftp_cmd_handle_cwd(p_thesession);
}
/*===============================================================================
function:返回上一级目录
===============================================================================*/
int myftp_cmd_handle_cdup(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
return -1;
}
chdir("..");
write(p_thesession->cmd_cntl_fd, "250 Directory successfully changed.\r\n",strlen("250 Directory successfully changed.\r\n"));
return -1;
}
/*===============================================================================
function:QUIT客户端退出登录命令处理函数
注:此命令表示控制连接也断开
===============================================================================*/
int myftp_cmd_handle_quit(pMYFTP_SESSION p_thesession)
{
write(p_thesession->cmd_cntl_fd, "221 Goodbye.\r\n", strlen("221 Goodbye.\r\n"));
return CHILD_QUIT;
}
/*===============================================================================
function:注销命令
注:此命令执行后,控制连接仍在,相当于电脑的注销后切换用户命令
===============================================================================*/
int myftp_cmd_handle_rein(pMYFTP_SESSION p_thesession)
{
int cmd_cntl_sock=p_thesession->cmd_cntl_fd;
write(p_thesession->cmd_cntl_fd, "220 Service ready for new user.\r\n", strlen("220 Service ready for new user.\r\n"));
return cmd_cntl_sock;
}
/*===============================================================================
function:port模式处理函数
注: port模式即为服务端产生一个新的数据套接字,主动去连接客户端的数据端口
===============================================================================*/
int myftp_cmd_handle_port(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
return -1;
}
myftp_arg_to_addr(p_thesession);
myftp_make_port_data_socket(p_thesession);
p_thesession->is_port_or_pasv=PORT;//TRUE===>port mode。
int client_sockfd=connect(p_thesession->port_data_fd, &p_thesession->data_sockaddr.un.u_sockaddr, sizeof(MYFTP_SOCKADDR));
if(-1==client_sockfd)
{
puts(p_thesession->p_cmd_line->cmd_line);
die("myftp_cmd_handle_port() connect error.\n");
}
write(p_thesession->cmd_cntl_fd,"200 PORT command successful. Consider using PASV.\r\n",strlen("200 PORT command successful. Consider using PASV.\r\n"));
return -1;
}
/*===============================================================================
function:将形如192.168.168.203的字符串转换为192,168,168,203字符串
===============================================================================*/
char* myftp_to_dotstr(char* p_str)
{
int i=0;
while (*(p_str+i))
{
if (*(p_str+i) == '.')
{
*(p_str+i) = ',';
}
i++;
}
return p_str;
}
/*===============================================================================
function:创建数据连接通道
注:过程==》创建流式套接字-根据控制连接套接字获取本机IP并存放与临时SOCKADDR结构中-制定该端口号为0-bind套接字到该地址上
再次获取本机ip以得到一个随机的端口号,从而完全生成了数据连接套接字。
===============================================================================*/
int myftp_make_pasv_data_fd(pMYFTP_SESSION p_thesession)
{
int pasv_data_sock=socket(AF_INET,SOCK_STREAM,0);//数据端口套节字;
if (-1==pasv_data_sock)
{
die("socket data error.\n");
}
MYFTP_SOCKADDR tmp_data_sock;
int len=sizeof(MYFTP_SOCKADDR);
getsockname(p_thesession->cmd_cntl_fd,&tmp_data_sock.un.u_sockaddr,&len);
tmp_data_sock.un.u_sockaddr_in.sin_port=0;
tmp_data_sock.un.u_sockaddr_in.sin_family=AF_INET;
if (-1==bind(pasv_data_sock,&tmp_data_sock.un.u_sockaddr,len))
{
die("bind error.\n");
}
getsockname(pasv_data_sock,&tmp_data_sock.un.u_sockaddr,&len);
p_thesession->local_addr=tmp_data_sock;
return p_thesession->pasv_listen_fd=pasv_data_sock;
}
/*===============================================================================
function:将数据传输通道(IP和port)的转换为形式如:192,168,100,201,12,241的字符串
注:端口号计算:12*256+241
===============================================================================*/
char* myftp_make_data_addr_str(char* tmp_pasv_addr_string, pMYFTP_SESSION p_thesession)
{
char data_trans_ip[16]={0};
strcpy(data_trans_ip, (char*)inet_ntoa(p_thesession->local_addr.un.u_sockaddr_in.sin_addr));
unsigned short port=ntohs(p_thesession->local_addr.un.u_sockaddr_in.sin_port);//必须将主机字节序转换为网络字节序,否则导致客户端在解释地址的时候出错,不能得到正确的端口
unsigned short a1=port/256;
unsigned short a2=port%256;
sprintf(tmp_pasv_addr_string,"227 Entering Passive Mode (%s,%d,%d)\r\n", myftp_to_dotstr(data_trans_ip),a1,a2);
return tmp_pasv_addr_string;
}
/*===============================================================================
function:PASV命令解释函数
备注:该模式编码过程--》创建一个新的数据连接套接字,将该套接字绑定到本机ip和一个随机端口号上
然后开始监听客户端。具体的accept函数在各个数据传输命令中实现
===============================================================================*/
int myftp_cmd_handle_pasv(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
return -1;
}
p_thesession->is_port_or_pasv=PASV;
int pasv_listen_sock=myftp_make_pasv_data_fd(p_thesession);
char tmp_pasv_addr_string[100]={0};
myftp_make_data_addr_str(tmp_pasv_addr_string,p_thesession);
write(p_thesession->cmd_cntl_fd, tmp_pasv_addr_string,strlen(tmp_pasv_addr_string));
if (-1==listen(pasv_listen_sock,LISTEN_BACKLOG))
{
die("myftp_cmd_handle_pasv listen error.\n");
}
return -1;
}
/*===============================================================================
function:文件传输类型命令TYPE解释函数
注:ASCII模式主要用于文本文件的传输;BINARY模式主要用于压缩文件、图像、音视频文件等的传输
===============================================================================*/
int myftp_cmd_handle_type(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
return -1;
}
if (strcmp(p_thesession->p_cmd_line->arg,"A")==0)
{
write(p_thesession->cmd_cntl_fd,"220 Switching to ASCII mode.\r\n",strlen("220 Switching to ASCII mode.\r\n"));
strcpy(p_thesession->typemode,"ASCII");
}
else if (strcmp(p_thesession->p_cmd_line->arg,"I")==0)
{
write(p_thesession->cmd_cntl_fd,"220 Switching to Binary mode.\r\n",strlen("220 Switching to Binary mode.\r\n"));
strcpy(p_thesession->typemode,"BINARY");
}
return -1;
}
/*===============================================================================
function:设置文件传输模式命令MODE解释函数。
注:默认为stream模式,其他模式未实现。
===============================================================================*/
int myftp_cmd_handle_mode(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
goto RETURN;
}
if (*p_thesession->p_cmd_line->arg == 'S'||*p_thesession->p_cmd_line->arg == 's')
{
write(p_thesession->cmd_cntl_fd, "200 Mode set to S.\r\n", strlen("200 Mode set to S.\r\n"));
}
else
{
write(p_thesession->cmd_cntl_fd, "504 Bad MODE command.\r\n", strlen("504 Bad MODE command.\r\n"));
}
RETURN:
return -1;
}
/*===============================================================================
function:打开服务器上客户需要下载的文件
===============================================================================*/
int myftp_get_downloadfile(pMYFTP_SESSION p_thesession)//打开要下载的文件。
{
if (-1 == access(p_thesession->p_cmd_line->arg, F_OK))//if file not exist
{
return FALSE;
}
else
{
int download_file_fd=-1;
if (-1 == (download_file_fd=open(p_thesession->p_cmd_line->arg, O_RDONLY)))
{
return FALSE;
}
return download_file_fd;
}
}
static unsigned long retr_num;//文件域变量,专用于统计下载数据时的传输量的记录
/*===============================================================================
function:下载文件子进程发送的信号的处理函数。当数据传输子进程结束时,进入该函数,对传输的数据量进程统计
reamrk:信号处理函数内部不能有不可重入函数出现,比如标准输入输出函数等
===============================================================================*/
void myftp_retr_file_stat(int signo, siginfo_t *info, void *pthesession)
{
retr_num += info->si_value.sival_int;
}
/*=================================================================================
function:下载文件命令RETR解释函数
注:其处理方式与其他数据传输命令类同,可参照之以理解
=================================================================================*/
int myftp_cmd_handle_retr(pMYFTP_SESSION p_thesession)
{
if (*p_thesession->user_str=='\0'||*p_thesession->passwd_str==0)
{
write(p_thesession->cmd_cntl_fd,"530 Not logged in.\r\n", strlen("530 Not logged in.\r\n"));
return -1;
}
struct sigaction sigact;
sigact.sa_flags=SA_SIGINFO;
sigact.sa_sigaction=myftp_retr_file_stat;
static unsigned int file_num;
int download_file_fd=myftp_get_downloadfile( p_thesession);
if (sigaction(SIGUSR2, &sigact, NULL)==-1)
{
die("download set sig handler error\n");
}
int trans_file_fd=fork();
switch (trans_file_fd)
{
case -1:
return -1;
case 0:
if (!download_file_fd)
{
write(p_thesession->cmd_cntl_fd, "550 Failed to open file.\r\n", strlen("550 Failed to open file.\r\n"));
exit(0);
}
else
{
struct stat sbuf;
if (-1 == lstat(p_thesession->p_cmd_line->arg, &sbuf))
{
write(p_thesession->cmd_cntl_fd, "550 Failed to open file.\r\n",strlen("550 Failed to open file.\r\n"));
exit(0);
}
else
{
char tmp_filesize[128]={0};
sprintf(tmp_filesize,"150 Opening BINARY mode data connection for %s (%d bytes).\r\n",p_thesession->p_cmd_line->arg, sbuf.st_size);
write(p_thesession->cmd_cntl_fd, tmp_filesize,strlen(tmp_filesize));
unsigned int read_count;
static unsigned int read_sum;
char data_buf[1024];
int len=sizeof(data_buf);
int tmp_data_fd=-1;
lseek(download_file_fd, (long)p_thesession->transfile_start_pos, SEEK_SET);
memset(data_buf, 0, len);
if (p_thesession->is_port_or_pasv==PORT)
{
tmp_data_fd=p_thesession->port_data_fd;
}
else
{
MYFTP_SOCKADDR tmp_data_addr;
int len=sizeof(MYFTP_SOCKADDR);
p_thesession->pasv_data_fd=accept(p_thesession->pasv_listen_fd,&tmp_data_addr.un.u_sockaddr,&len);
if(p_thesession->pasv_data_fd==-1)
{
#ifdef __MYDEBUG
perror("RETR accept client_id");
#endif
}
close(p_thesession->pasv_listen_fd);
tmp_data_fd=p_thesession->pasv_data_fd;
}
unsigned long int i=0;
while (read_count=read(download_file_fd, data_buf, len))
{
i++;
if (!(i%myftp_conf.file_trans_rate))
usleep(50*10000+50*10000-10*1000);//这个限速比较精确,浮动范围为-1%~+1%
int write_count=write(tmp_data_fd, data_buf, read_count);
read_sum+=read_count;
memset(data_buf, 0, len);
}
close(tmp_data_fd);
write(p_thesession->cmd_cntl_fd, "226 File send OK.\r\n", strlen("226 File send OK.\r\n"));
close(p_thesession->cmd_cntl_fd);
sigval_t val;
val.sival_int=read_sum;
if(sigqueue(getppid(),SIGUSR2,val)==-1)
{
die("upload sigqueue error\n");
}
exit(1);
}
}
break;
default:
p_thesession->down_file_num=++file_num;
#ifdef __MYDEBUG
printf("download file num %d\n",p_thesession->down_file_num);
printf("farther proc download num %d bytes\n",p_thesession->size_of_retrfile);
#endif
close(download_file_fd);
p_thesession->data_trans_child_pid=trans_file_fd;
close(p_thesession->port_data_fd);
close(p_thesession->pasv_data_fd);
return -1;
}
}
/*=======================================================================
function:创建客户要上传的文件
=======================================================================*/
int myftp_creat_transfile(pMYFTP_SESSION p_thesession)//客户在上传文件时,使用之
{
int stor_file=-1;
if (-1 == access(p_thesession->p_cmd_line->arg, F_OK))//if file not exist
{
stor_file = open(p_thesession->p_cmd_line->arg, O_CREAT|O_EXCL|O_RDWR, USER_MASK);
}
else
{
stor_file = open(p_thesession->p_cmd_line->arg, O_RDWR, USER_MASK);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -