三、服务器功能实现与关键函数
1.头文件信息
头文件ftp.h包含系统信息:
#ifndef __FTP_H_
#define __FTP_H_
#define COMMAND_NUM 7
#define FILE_TRANS_MODE_ASIC 0
#define FILE_TRANS_MODE_BIN 1
char clientCommand[COMMAND_NUM][20]={{"pwd"}, {"quit"},{'?'},{"ls"},{"cd"}, {"get"},{"put"}};
char serverInfo220[]="220 myFTP Server ready.../r/n";
char serverInfo230[]="230 User logged in, proceed./r/n";
char serverInfo331[]="331 User name okay, need password./r/n";
char serverInfo221[]="221 Goodbye!/r/n";
char serverInfo150[]="150 File status okay; about to open data connection./r/n";
char serverInfo226[]="226 Closing data connection./r/n";
char serverInfo200[]="200 Command okay./r/n";
char serverInfo215[]="215 Unix Type FC5./r/n";
char serverInfo213[]="213 File status./r/n";
char serverInfo211[]="211 System status, or system help reply./r/n";
char serverInfo350[]="350 Requested file action pending further information./r/n";
char serverInfo530[]="530 Not logged in./r/n";
char serverInfo531[]="531 Not root client. Anonymous client./r/n";
char serverInfo[]="202 Command not implemented, superfluous at this site./r/n";
#endif
2.处理客户端命令
定义权限用户名及密码:
const char default_user[] = "root";
const char default_pass[] = "1234";
定义匿名用户及密码:
const char anony_user[]="anonymous";
const char anony_pass[]="anonymous";
函数*Handle_Client_Request(void* arg)处理客户需求,从客户端连接成功开始到结束服务。其中调用do_client_work(info->client_sock,info->client)函数与客户端交互。
void *Handle_Client_Request(void* arg)
{
struct ARG*info;
info=(struct ARG*)arg;
printf("You got a connection from %s/n",inet_ntoa(info->client.sin_addr));
do_client_work(info->client_sock,info->client);
close(info->client_sock);
pthread_exit(NULL);
}
函数do_client_work(int client_sock,struct sockaddr_in client)处理FTP各种命令。
void do_client_work(int client_sock,struct sockaddr_in client)
{
int login_flag;
login_flag=login(client_sock);
while(recv_client_info(client_sock)&&login_flag==1)//缺省用户操作响应
{
if((strncmp("quit", client_Control_Info, 4) == 0)||(strncmp("QUIT", client_Control_Info, 4) == 0))
{
send_client_info(client_sock, serverInfo221, strlen(serverInfo221));
break;
}
else if((strncmp("close",client_Control_Info,5) == 0)||(strncmp("CLOSE",client_Control_Info,5) == 0))
{
printf("Client Quit!/n");
shutdown(client_sock,SHUT_WR);
//关闭连接服务器,只关闭写操作。此时仍可进行客户端相关操作。
}
else if(strncmp("pwd", client_Control_Info, 3) == 0||(strncmp("PWD", client_Control_Info, 3) == 0))
{
char pwd_info[MSG_INFO];
char tmp_dir[DIR_INFO];
snprintf(pwd_info, MSG_INFO, "257 /"%s/" is current location./r/n", getcwd(tmp_dir, DIR_INFO));
send_client_info(client_sock, pwd_info, strlen(pwd_info));
}
else if(strncmp("cwd", client_Control_Info, 3) == 0||(strncmp("CWD", client_Control_Info, 3) == 0))
{
handle_cwd(client_sock);
}
else if(strncmp("mkd", client_Control_Info, 3) == 0||(strncmp("MKD", client_Control_Info, 3) == 0))
{
handle_mkd(client_sock);
}
else if(strncmp("rmd", client_Control_Info, 3) == 0||(strncmp("RMD", client_Control_Info, 3) == 0))
{
handle_rmd(client_sock);
}
else if(strncmp("dele", client_Control_Info, 4) == 0||(strncmp("DELE", client_Control_Info, 4) == 0))
{
handle_del(client_sock);
}
else if(strncmp("pasv", client_Control_Info, 4) == 0||(strncmp("PASV", client_Control_Info, 4) == 0))
{
handle_pasv(client_sock,client);
}
else if(strncmp("list", client_Control_Info, 4) == 0||(strncmp("LIST", client_Control_Info, 4) == 0))
{
handle_list(client_sock);
send_client_info(client_sock,serverInfo226, strlen(serverInfo226));
}
else if(strncmp("type", client_Control_Info, 4) == 0||(strncmp("TYPE", client_Control_Info, 4) == 0))
{
if(strncmp("type I", client_Control_Info, 6) == 0||(strncmp("TYPE I", client_Control_Info, 6) == 0))
translate_data_mode=FILE_TRANS_MODE_BIN;
send_client_info(client_sock, serverInfo200, strlen(serverInfo200));
}
else if(strncmp("retr", client_Control_Info, 4) == 0||(strncmp("RETR", client_Control_Info, 4) == 0))
{
handle_file(client_sock);
send_client_info(client_sock,serverInfo226, strlen(serverInfo226));
}
else if(strncmp("stor", client_Control_Info, 4) == 0||(strncmp("STOR", client_Control_Info, 4) == 0))
{
handle_file(client_sock);
send_client_info(client_sock,serverInfo226, strlen(serverInfo226));
}
else if(strncmp("syst", client_Control_Info, 4) == 0||(strncmp("SYST", client_Control_Info, 4) == 0))
{
send_client_info(client_sock, serverInfo215, strlen(serverInfo215));
}
else if(strncmp("size", client_Control_Info, 4) == 0||(strncmp("SIZE", client_Control_Info, 4) == 0))
{
send_client_info(client_sock, serverInfo213, strlen(serverInfo213));
}
else if(strncmp("feat", client_Control_Info, 4) == 0||(strncmp("FEAT", client_Control_Info, 4) == 0))
{
send_client_info(client_sock, serverInfo211, strlen(serverInfo211));
}
else if(strncmp("rest", client_Control_Info, 4) == 0||(strncmp("REST", client_Control_Info, 4) == 0))
{
send_client_info(client_sock, serverInfo350, strlen(serverInfo350));
}
else
{
send_client_info(client_sock, serverInfo, strlen(serverInfo));
}
}
while(recv_client_info(client_sock)&&(login_flag == 2))//匿名用户操作响应
{
//省略代码
}
}
函数int login(int client_sock)为登陆函数。处理客户端登录请求,与已定义的权限用户密码进行匹配,成功则为ROOT用户,失败则匹配匿名用户,匿名用户只能进行简单服务。
if(strncmp(format_client_Info, default_user, 4) == 0)
{
flag=1;
}
if(strncmp(format_client_Info, anony_user, 9) == 0)
{
flag=2;
}
函数void handle_cwd(int client_sock)处理转换目录功能。
关键代码:
if (chdir(client_dir) >= 0)
{
snprintf(cwd_info, MSG_INFO, "257 /"%s/" is current location./r/n", getcwd(tmp_dir, DIR_INFO));
send_client_info(client_sock, cwd_info, strlen(cwd_info));
}
else
{
snprintf(cwd_info, MSG_INFO, "550 %s :%s/r/n",client_dir,strerror(errno));
perror("chdir():");
send_client_info(client_sock, cwd_info, strlen(cwd_info));
}
上述代码表示当转换目录存在则把当前目录更改为转换目录,否则报错。
创建目录与删除目录关键代码与之类似。
函数void handle_mkd(int client_sock)处理创建目录功能。
关键代码:
if (mkdir(client_dir) >= 0)
{
printf(" /"%s/" is created successfully./r/n", client_dir);
send_client_info(client_sock, mkd_info, strlen(mkd_info));
}
else
{
snprintf(mkd_info, MSG_INFO, "550 %s :%s/r/n",client_dir,strerror(errno));
perror("mkdir():");
send_client_info(client_sock, mkd_info, strlen(mkd_info));
}
上述代码表示创建client_dir目录,成功输出消息,否则报错。
函数void handle_rmd(int client_sock)处理删除目录功能。
关键代码:
if (rmdir(client_dir) >= 0)
{
printf(" /"%s/" is deleted successfully./r/n", client_dir);
send_client_info(client_sock, rmd_info, strlen(rmd_info));
}
else
{
snprintf(rmd_info, MSG_INFO, "550 %s :%s/r/n",client_dir,strerror(errno));
perror("rmdir():");
send_client_info(client_sock, rmd_info, strlen(rmd_info));
}
上述代码表示删除client_dir目录,成功输出消息,否则报错。
函数handle_list(int client_sock)处理list命令。
FILE *pipe_fp;//建立管道以传输数据
char t_dir[DIR_INFO];
char list_cmd_info[DIR_INFO];
snprintf(list_cmd_info, DIR_INFO, "ls -l %s", getcwd(t_dir, DIR_INFO));//得到当前目录
if ((pipe_fp = popen(list_cmd_info, "r")) == NULL)//打开管道
{
printf("pipe open error in cmd_list/n");
return ;
}
printf("pipe open successfully!, cmd is %s/n", list_cmd_info);
char t_char;
while ((t_char = fgetc(pipe_fp)) != EOF)
{
printf("%c", t_char);
write(t_data_sock, &t_char, 1);
}//得到当前目录文件列表
pclose(pipe_fp);//关闭管道
printf("close pipe successfully!/n");
close(t_data_sock);
printf("%s close data successfully!/n",serverInfo226);
close(ftp_data_sock);
函数struct sockaddr_in create_date_sock()创建数据sock。
struct sockaddr_in create_date_sock()
{
int t_client_sock;
struct sockaddr_in t_data_addr;
t_client_sock = socket(AF_INET, SOCK_STREAM, 0);//创建客户数据SOCK
if (t_client_sock < 0)
{
printf("create data socket error!/n");
return;
}
srand((int)time(0));
int a=rand()%1000+1025;
bzero(&t_data_addr, sizeof(t_data_addr));
t_data_addr.sin_family = AF_INET;
t_data_addr.sin_addr.s_addr = htonl(INADDR_ANY);
t_data_addr.sin_port = htons(a);
if (bind(t_client_sock, (struct sockaddr*)&t_data_addr, sizeof(struct sockaddr)) < 0)
{
printf("bind error in create data socket:%s/n",strerror(errno));
return;
}
listen(t_client_sock, LISTEN_QENU);
ftp_data_sock=t_client_sock;
return t_data_addr;
}
3.处理文件类命令
函数void handle_file(int client_sock)处理文件功能。把上传下载功能集成在一个函数中实现。
关键代码:
FILE* fp;//建立管道处理文件信息
int file_fd;
int n;
char t_dir[DIR_INFO];
char file_info[DIR_INFO];
snprintf(file_info, DIR_INFO, "%s/%s", getcwd(t_dir, DIR_INFO),format_client_Info);
//获得文件信息
if(strncmp(getcwd(t_dir, DIR_INFO),format_client_Info,strlen(getcwd(t_dir, DIR_INFO))-1)==0)
fp = fopen(format_client_Info, file_mode); //打开文件
else
fp = fopen(file_info, file_mode);
if(strncmp("retr", client_Control_Info, 4) == 0||(strncmp("RETR", client_Control_Info, 4) == 0))
{
while ((n = read(cmd_sock, client_Data_Info, MAX_INFO)) > 0)
{//读写文件
if (write(t_data_sock, client_Data_Info, n) != n)
{
printf("retr transfer error/n");
return;
}
}
}
else
{
while ((n = read(t_data_sock, client_Data_Info, MAX_INFO)) > 0)
{
if (write(cmd_sock, client_Data_Info, n) != n)
{
printf("stor transfer error/n");
return;
}
}
}
fclose(fp); //关闭传输管道
close(t_data_sock); //关闭数据sock
close(ftp_data_sock); //关闭FTPsock
函数void send_client_info(int client_sock,char* info,int length)发送客户端信息。
函数int recv_client_info(int client_sock)接收客户端信息。
void send_client_info(int client_sock,char* info,int length)
{
int len;
if((len = send(client_sock, info, length,0))<0)
{
perror("send info error ");
return;
}
}
int recv_client_info(int client_sock)
{
int num;
if((num=recv(client_sock,client_Control_Info,MAX_INFO,0))<0)
{
perror("receive info error ");
return;
}
client_Control_Info[num]='/0';
printf("Client %d Message:%s/n",pthread_self(),client_Control_Info);
if(strncmp("USER", client_Control_Info, 4) == 0||strncmp("user", client_Control_Info, 4) == 0)return 2;
return 1;
}
函数void handle_del(int client_sock)处理删除目录功能。
关键代码:
if (unlink(client_file) >= 0)
{
printf(" /"%s/" is deleted successfully./r/n", client_file);
send_client_info(client_sock, del_info, strlen(del_info));
}
else
{
snprintf(del_info, MSG_INFO, "550 %s :%s/r/n",client_file,strerror(errno));
perror("unlink():");
send_client_info(client_sock, del_info, strlen(del_info));
}
上述代码表示删除文件client_file,成功输出消息,否则报错