FTP服务器的实现(C语言)“华体会”

发布时间:2021-11-25 22:10 阅读次数:
本文摘要:我们在之前的文章中,曾经对FTP文件传输协议做过详细的先容。本章,我们对如何用C语言实现FTP服务器做一个简朴的先容。 概述FTP文件传输协议,是因特网上使用得最广泛的文件传输协议。FTP提供交互式的会见,允许客户指明文件的花样与类型,并允许文件具有存储权限。FTP屏蔽了差别操作系统之前的细节,因此适合在异构网络中任意盘算机之间传送文件。

华体会官网

我们在之前的文章中,曾经对FTP文件传输协议做过详细的先容。本章,我们对如何用C语言实现FTP服务器做一个简朴的先容。

概述FTP文件传输协议,是因特网上使用得最广泛的文件传输协议。FTP提供交互式的会见,允许客户指明文件的花样与类型,并允许文件具有存储权限。FTP屏蔽了差别操作系统之前的细节,因此适合在异构网络中任意盘算机之间传送文件。

FTP的基本事情原理FTP使用C/S方式,一个FTP服务器可以为多个客户历程提供服务,FTP服务器历程由两大部门组成:一个主历程,卖力吸收新的请求;另外有若干个附属历程,卖力处置惩罚单个请求。主历程的事情步骤如下:打开端口号(一般为21),使客户端能通过此端口号会见;等候客户端发出毗连请求;启动附属历程来处置惩罚客户历程发来的请求。附属历程对客户历程的请求处置惩罚完后即终止,附属历程在运行期间可能会凭据需要另外建立其他一些历程。

回到等候状态,继续等候其他客户历程发来的毗连请求。主历程和附属历程是并发举行的。在举行文件传输时,FTP的客户和服务器之间要建设两个并行的TCP毗连:“控制毗连”和“数据毗连”。

控制毗连在整个会话期间一直保持打开,FTP客户所发出的传送请求,通过控制毗连发送给服务器端的控制历程,可是控制毗连并不会用于传输数据。实际传输文件的是“数据毗连”。

服务器端的控制历程在吸收到FTP客户发送来的文件传输请求后,就会建立“数据传送历程”和“数据毗连”,用来毗连客户端和服务器端的数据传送历程。由于FTP使用了一个分散的控制毗连,因此FTP的控制信息是带外控制的。当客户历程向服务器历程发出建设毗连请求时,通过服务器端口号21请求毗连,同时会告诉服务器历程自己用于建设数据传送毗连的另一个端口号。

服务器一般使用端口号20同客户历程建设数据毗连,由于FTP使用两个差别的端口号,所以数据毗连和控制毗连不会发生杂乱。综上所述,我们可以画出基本的算法流程图代码实现:首先是基本的界说/* Commands enumeration */typedef enum cmdlist { ABOR, CWD, DELE, LIST, MDTM, MKD, NLST, PASS, PASV, PORT, PWD, QUIT, RETR, RMD, RNFR, RNTO, SITE, SIZE, STOR, TYPE, USER, NOOP} cmdlist;/* String mappings for cmdlist */static const char *cmdlist_str[] = { "ABOR", "CWD", "DELE", "LIST", "MDTM", "MKD", "NLST", "PASS", "PASV", "PORT", "PWD", "QUIT", "RETR", "RMD", "RNFR", "RNTO", "SITE", "SIZE", "STOR", "TYPE", "USER", "NOOP" };控制端口的界说/* define FTP control port */#define CONTROLPORT 21主函数/** * Sets up server and handles incoming connections * @param port Server port */int main(){ int sock = create_socket(CONTROLPORT ); struct sockaddr_in client_address; int len = sizeof(client_address); int connection, pid, bytes_read; while(1){ connection = accept(sock, (struct sockaddr*) &client_address,&len); char buffer[BSIZE]; Command *cmd = malloc(sizeof(Command)); State *state = malloc(sizeof(State)); pid = fork(); memset(buffer,0,BSIZE); if(pid<0){ fprintf(stderr, "Cannot create child process."); exit(EXIT_FAILURE); } if(pid==0){ close(sock); char welcome[BSIZE] = "220 "; if(strlen(welcome_message)<BSIZE-4){ strcat(welcome,welcome_message); } else{ strcat(welcome, "Welcome to nice FTP service."); } /* Write welcome message */ strcat(welcome,"n"); write(connection, welcome,strlen(welcome)); /* Read commands from client */ while (bytes_read = read(connection,buffer,BSIZE)){ signal(SIGCHLD,my_wait); if(!(bytes_read>BSIZE)){ /* TODO: output this to log */ buffer[BSIZE-1] = ''; printf("User %s sent command: %sn",(state->username==0)?"unknown":state->username,buffer); parse_command(buffer,cmd); state->connection = connection; /* Ignore non-ascii char. Ignores telnet command */ if(buffer[0]<=127 || buffer[0]>=0){ response(cmd,state); } memset(buffer,0,BSIZE); memset(cmd,0,sizeof(cmd)); } else{ /* Read error */ perror("server:read"); } } printf("Client disconnected.n"); exit(0); }else{ printf("closing... :(n"); close(connection); } }}功效的实现/** * Handle USER command * @param cmd Command with args * @param state Current client connection state */void ftp_user(Command *cmd, State *state){ const int total_usernames = sizeof(usernames)/sizeof(char *); if(lookup(cmd->arg,usernames,total_usernames)>=0){ state->username = malloc(32); memset(state->username,0,32); strcpy(state->username,cmd->arg); state->username_ok = 1; state->message = "331 User name okay, need passwordn"; }else{ state->message = "530 Invalid usernamen"; } write_state(state);}/** PASS command */void ftp_pass(Command *cmd, State *state){ if(state->username_ok==1){ state->logged_in = 1; state->message = "230 Login successfuln"; }else{ state->message = "500 Invalid username or passwordn"; } write_state(state);}/** PASV command */void ftp_pasv(Command *cmd, State *state){ if(state->logged_in){ int ip[4]; char buff[255]; char *response = "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)n"; Port *port = malloc(sizeof(Port)); gen_port(port); getip(state->connection,ip); /* Close previous passive socket? */ close(state->sock_pasv); /* Start listening here, but don't accept the connection */ state->sock_pasv = create_socket((256*port->p1)+port->p2); printf("port: %dn",256*port->p1+port->p2); sprintf(buff,response,ip[0],ip[1],ip[2],ip[3],port->p1,port->p2); state->message = buff; state->mode = SERVER; puts(state->message); }else{ state->message = "530 Please login with USER and PASS.n"; printf("%s",state->message); } write_state(state);}/** LIST command */void ftp_list(Command *cmd, State *state){ if(state->logged_in==1){ struct dirent *entry; struct stat statbuf; struct tm *time; char timebuff[80], current_dir[BSIZE]; int connection; time_t rawtime; /* TODO: dynamic buffering maybe? */ char cwd[BSIZE], cwd_orig[BSIZE]; memset(cwd,0,BSIZE); memset(cwd_orig,0,BSIZE); /* Later we want to go to the original path */ getcwd(cwd_orig,BSIZE); /* Just chdir to specified path */ if(strlen(cmd->arg)>0&&cmd->arg[0]!='-'){ chdir(cmd->arg); } getcwd(cwd,BSIZE); DIR *dp = opendir(cwd); if(!dp){ state->message = "550 Failed to open directory.n"; }else{ if(state->mode == SERVER){ connection = accept_connection(state->sock_pasv); state->message = "150 Here comes the directory listing.n"; puts(state->message); while(entry=readdir(dp)){ if(stat(entry->d_name,&statbuf)==-1){ fprintf(stderr, "FTP: Error reading file stats...n"); }else{ char *perms = malloc(9); memset(perms,0,9); /* Convert time_t to tm struct */ rawtime = statbuf.st_mtime; time = localtime(&rawtime); strftime(timebuff,80,"%b %d %H:%M",time); str_perm((statbuf.st_mode & ALLPERMS), perms); dprintf(connection, "%c%s %5d %4d %4d %8d %s %srn", (entry->d_type==DT_DIR)?'d':'-', perms,statbuf.st_nlink, statbuf.st_uid, statbuf.st_gid, statbuf.st_size, timebuff, entry->d_name); } } write_state(state); state->message = "226 Directory send OK.n"; state->mode = NORMAL; close(connection); close(state->sock_pasv); }else if(state->mode == CLIENT){ state->message = "502 Command not implemented.n"; }else{ state->message = "425 Use PASV or PORT first.n"; } } closedir(dp); chdir(cwd_orig); }else{ state->message = "530 Please login with USER and PASS.n"; } state->mode = NORMAL; write_state(state);}/** QUIT command */void ftp_quit(State *state){ state->message = "221 Goodbye, friend. I never thought I'd die like this.n"; write_state(state); close(state->connection); exit(0);}/** PWD command */void ftp_pwd(Command *cmd, State *state){ if(state->logged_in){ char cwd[BSIZE]; char result[BSIZE]; memset(result, 0, BSIZE); if(getcwd(cwd,BSIZE)!=NULL){ strcat(result,"257 ""); strcat(result,cwd); strcat(result,""n"); state->message = result; }else{ state->message = "550 Failed to get pwd.n"; } write_state(state); }}/** CWD command */void ftp_cwd(Command *cmd, State *state){ if(state->logged_in){ if(chdir(cmd->arg)==0){ state->message = "250 Directory successfully changed.n"; }else{ state->message = "550 Failed to change directory.n"; } }else{ state->message = "500 Login with USER and PASS.n"; } write_state(state);}/** * MKD command * TODO: full path directory creation */void ftp_mkd(Command *cmd, State *state){ if(state->logged_in){ char cwd[BSIZE]; char res[BSIZE]; memset(cwd,0,BSIZE); memset(res,0,BSIZE); getcwd(cwd,BSIZE); /* TODO: check if directory already exists with chdir? */ /* Absolute path */ if(cmd->arg[0]=='/'){ if(mkdir(cmd->arg,S_IRWXU)==0){ strcat(res,"257 ""); strcat(res,cmd->arg); strcat(res,"" new directory created.n"); state->message = res; }else{ state->message = "550 Failed to create directory. Check path or permissions.n"; } } /* Relative path */ else{ if(mkdir(cmd->arg,S_IRWXU)==0){ sprintf(res,"257 "%s/%s" new directory created.n",cwd,cmd->arg); state->message = res; }else{ state->message = "550 Failed to create directory.n"; } } }else{ state->message = "500 Good news, everyone! There's a report on TV with some very bad news!n"; } write_state(state);}/** RETR command */void ftp_retr(Command *cmd, State *state){ if(fork()==0){ int connection; int fd; struct stat stat_buf; off_t offset = 0; int sent_total = 0; if(state->logged_in){ /* Passive mode */ if(state->mode == SERVER){ if(access(cmd->arg,R_OK)==0 && (fd = open(cmd->arg,O_RDONLY))){ fstat(fd,&stat_buf); state->message = "150 Opening BINARY mode data connection.n"; write_state(state); connection = accept_connection(state->sock_pasv); close(state->sock_pasv); if(sent_total = sendfile(connection, fd, &offset, stat_buf.st_size)){ if(sent_total != stat_buf.st_size){ perror("ftp_retr:sendfile"); exit(EXIT_SUCCESS); } state->message = "226 File send OK.n"; }else{ state->message = "550 Failed to read file.n"; } }else{ state->message = "550 Failed to get filen"; } }else{ state->message = "550 Please use PASV instead of PORT.n"; } }else{ state->message = "530 Please login with USER and PASS.n"; } close(fd); close(connection); write_state(state); exit(EXIT_SUCCESS); } state->mode = NORMAL; close(state->sock_pasv);}/** Handle STOR command. TODO: check permissions. */void ftp_stor(Command *cmd, State *state){ if(fork()==0){ int connection, fd; off_t offset = 0; int pipefd[2]; int res = 1; const int buff_size = 8192; FILE *fp = fopen(cmd->arg,"w"); if(fp==NULL){ /* TODO: write status message here! */ perror("ftp_stor:fopen"); }else if(state->logged_in){ if(!(state->mode==SERVER)){ state->message = "550 Please use PASV instead of PORT.n"; } /* Passive mode */ else{ fd = fileno(fp); connection = accept_connection(state->sock_pasv); close(state->sock_pasv); if(pipe(pipefd)==-1)perror("ftp_stor: pipe"); state->message = "125 Data connection already open; transfer starting.n"; write_state(state); /* Using splice function for file receiving. * The splice() system call first appeared in Linux 2.6.17. */ while ((res = splice(connection, 0, pipefd[1], NULL, buff_size, SPLICE_F_MORE | SPLICE_F_MOVE))>0){ splice(pipefd[0], NULL, fd, 0, buff_size, SPLICE_F_MORE | SPLICE_F_MOVE); } /* TODO: signal with ABOR command to exit */ /* Internal error */ if(res==-1){ perror("ftp_stor: splice"); exit(EXIT_SUCCESS); }else{ state->message = "226 File send OK.n"; } close(connection); close(fd); } }else{ state->message = "530 Please login with USER and PASS.n"; } close(connection); write_state(state); exit(EXIT_SUCCESS); } state->mode = NORMAL; close(state->sock_pasv);}/** ABOR command */void ftp_abor(State *state){ if(state->logged_in){ state->message = "226 Closing data connection.n"; state->message = "225 Data connection open; no transfer in progress.n"; }else{ state->message = "530 Please login with USER and PASS.n"; } write_state(state);}/** * Handle TYPE command. * BINARY only at the moment. */void ftp_type(Command *cmd,State *state){ if(state->logged_in){ if(cmd->arg[0]=='I'){ state->message = "200 Switching to Binary mode.n"; }else if(cmd->arg[0]=='A'){ /* Type A must be always accepted according to RFC */ state->message = "200 Switching to ASCII mode.n"; }else{ state->message = "504 Command not implemented for that parameter.n"; } }else{ state->message = "530 Please login with USER and PASS.n"; } write_state(state);}/** Handle DELE command */void ftp_dele(Command *cmd,State *state){ if(state->logged_in){ if(unlink(cmd->arg)==-1){ state->message = "550 File unavailable.n"; }else{ state->message = "250 Requested file action okay, completed.n"; } }else{ state->message = "530 Please login with USER and PASS.n"; } write_state(state);}/** Handle RMD */void ftp_rmd(Command *cmd, State *state){ if(!state->logged_in){ state->message = "530 Please login first.n"; }else{ if(rmdir(cmd->arg)==0){ state->message = "250 Requested file action okay, completed.n"; }else{ state->message = "550 Cannot delete directory.n"; } } write_state(state);}/** Handle SIZE (RFC 3659) */void ftp_size(Command *cmd, State *state){ if(state->logged_in){ struct stat statbuf; char filesize[128]; memset(filesize,0,128); /* Success */ if(stat(cmd->arg,&statbuf)==0){ sprintf(filesize, "213 %dn", statbuf.st_size); state->message = filesize; }else{ state->message = "550 Could not get file size.n"; } }else{ state->message = "530 Please login with USER and PASS.n"; } write_state(state);}。


本文关键词:华体会官网,FTP,服务器,的,实现,语言,“,华体会,”,我们

本文来源:华体会-www.cnyonglong.com

在线客服 联系方式 二维码

电话

0678-744019190

扫一扫,关注我们