欢迎访问欧亿体育app·中国有限公司

欧亿体育app·中国有限公司

欧亿体育app·中国有限公司

提供设计,生产,安装,售后服务一条龙服务

020-88888888
企业风采
您的位置: 主页 > 关于我们 > 企业风采
FTP服务器的实现(C语言)【欧亿体育app】
发布时间:2023-11-16 22:53浏览次数:
本文摘要:我们在之前的文章中,曾经对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)){ /* TODO: output this to log */ buffer[BSIZE-1] = '\0'; printf("User %s sent command: %s\n",(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 password\n"; }else{ state->message = "530 Invalid username\n"; } 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 successful\n"; }else{ state->message = "500 Invalid username or password\n"; } 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: %d\n",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 %s\r\n", (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 file\n"; } }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 %d\n", 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);}。


本文关键词:欧亿体育app

本文来源:欧亿体育app-www.pronunciationtips.com

020-88888888