c语言实现tcp 服务器设计_FTP服务器的实现(C语言)

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

06ea188444fe0beccf55715179f41d40.png

概述

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

FTP的基本工作原理

FTP使用C/S方式,一个FTP服务器可以为多个客户进程提供服务,FTP服务器进程由两大部分组成:一个主进程,负责接收新的请求;另外有若干个从属进程,负责处理单个请求。

主进程的工作步骤如下:

  1. 打开端口号(一般为21),使客户端能通过此端口号访问;
  2. 等待客户端发出连接请求;
  3. 启动从属进程来处理客户进程发来的请求。从属进程对客户进程的请求处理完后即终止,从属进程在运行期间可能会根据需要另外创建其他一些进程。
  4. 回到等待状态,继续等待其他客户进程发来的连接请求。主进程和从属进程是并发进行的。

在进行文件传输时,FTP的客户和服务器之间要建立两个并行的TCP连接:“控制连接”和“数据连接”。控制连接在整个会话期间一直保持打开,FTP客户所发出的传送请求,通过控制连接发送给服务器端的控制进程,但是控制连接并不会用于传输数据。实际传输文件的是“数据连接”。服务器端的控制进程在接收到FTP客户发送来的文件传输请求后,就会创建“数据传送进程”“数据连接”,用来连接客户端和服务器端的数据传送进程。由于FTP使用了一个分离的控制连接,因此FTP的控制信息是带外控制的。

当客户进程向服务器进程发出建立连接请求时,通过服务器端口号21请求连接,同时会告诉服务器进程自己用于建立数据传送连接的另一个端口号。服务器一般使用端口号20同客户进程建立数据连接,由于FTP使用两个不同的端口号,所以数据连接和控制连接不会发生混乱。

82d06df09f2d04f3d0be70d705251038.png

综上所述,我们可以画出基本的算法流程图

70ac5267f83294f0bbdcdbb48ee74ab8.png

代码实现:

  • 首先是基本的定义
/* 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",(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.");         exit(0);    }else{      printf("closing... :(");      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";  }else{    state->message = "530 Invalid username";  }  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";  }else{    state->message = "500 Invalid username or password";  }  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)";    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",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.";    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.";    }else{      if(state->mode == SERVER){        connection = accept_connection(state->sock_pasv);        state->message = "150 Here comes the directory listing.";        puts(state->message);        while(entry=readdir(dp)){          if(stat(entry->d_name,&statbuf)==-1){            fprintf(stderr, "FTP: Error reading file stats...");          }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",                 (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.";        state->mode = NORMAL;        close(connection);        close(state->sock_pasv);      }else if(state->mode == CLIENT){        state->message = "502 Command not implemented.";      }else{        state->message = "425 Use PASV or PORT first.";      }    }    closedir(dp);    chdir(cwd_orig);  }else{    state->message = "530 Please login with USER and PASS.";  }  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.";  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,""");      state->message = result;    }else{      state->message = "550 Failed to get pwd.";    }    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.";    }else{      state->message = "550 Failed to change directory.";    }  }else{    state->message = "500 Login with USER and PASS.";  }  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.");        state->message = res;      }else{        state->message = "550 Failed to create directory. Check path or permissions.";      }    }    /* Relative path */    else{      if(mkdir(cmd->arg,S_IRWXU)==0){        sprintf(res,"257 "%s/%s" new directory created.",cwd,cmd->arg);        state->message = res;      }else{        state->message = "550 Failed to create directory.";      }    }  }else{    state->message = "500 Good news, everyone! There's a report on TV with some very bad news!";  }  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.";                    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.";          }else{            state->message = "550 Failed to read file.";          }        }else{          state->message = "550 Failed to get file";        }      }else{        state->message = "550 Please use PASV instead of PORT.";      }    }else{      state->message = "530 Please login with USER and PASS.";    }    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.";      }      /* 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.";        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.";        }        close(connection);        close(fd);      }    }else{      state->message = "530 Please login with USER and PASS.";    }    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.";    state->message = "225 Data connection open; no transfer in progress.";  }else{    state->message = "530 Please login with USER and PASS.";  }  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.";    }else if(cmd->arg[0]=='A'){      /* Type A must be always accepted according to RFC */      state->message = "200 Switching to ASCII mode.";    }else{      state->message = "504 Command not implemented for that parameter.";    }  }else{    state->message = "530 Please login with USER and PASS.";  }  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.";    }else{      state->message = "250 Requested file action okay, completed.";    }  }else{    state->message = "530 Please login with USER and PASS.";  }  write_state(state);}/** Handle RMD */void ftp_rmd(Command *cmd, State *state){  if(!state->logged_in){    state->message = "530 Please login first.";  }else{    if(rmdir(cmd->arg)==0){      state->message = "250 Requested file action okay, completed.";    }else{      state->message = "550 Cannot delete directory.";    }  }  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", statbuf.st_size);      state->message = filesize;    }else{      state->message = "550 Could not get file size.";    }  }else{    state->message = "530 Please login with USER and PASS.";  }  write_state(state);}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C语言可以用来创建FTP(文件传输协议)客户端和服务器。FTP是一种用于在计算机之间进行文件传输的标准协议,它允许用户通过网络上传和下载文件。 对于FTP客户端,可以使用C语言的socket编程来创建一个连接到FTP服务器的套接字。使用套接字来与服务器建立连接后,可以使用C语言的网络编程函数来实现FTP命令的发送和接收。例如,可以使用C语言的send和recv函数来发送和接收FTP命令和数据。 对于FTP服务器,可以使用C语言的socket编程来创建一个监听特定连接请求的套接字。当客户端连接到服务器时,服务器可以使用C语言的网络编程函数来处理客户端的FTP命令和数据。服务器可以使用C语言的文件操作函数来读写文件,并使用FTP命令在客户端和服务器之间进行传输。 为了建立一个完整的FTP客户端和服务器,还需要实现一些FTP命令,如登录、文件上传、文件下载等。这些命令可以使用C语言编写的函数来处理。FTP客户端和服务器之间的通信可以通过套接字进行,可以使用C语言的字符串处理函数来解析和处理FTP命令和数据。 总之,通过使用C语言的socket编程功能和网络编程函数,可以创建一个FTP客户端和服务器。使用C语言提供的文件操作函数和字符串处理函数,可以实现FTP命令的处理和文件传输。 ### 回答2: 创建C语言FTP客户端和服务器主要涉及以下几个步骤: 1. FTP服务器端的创建: - 创建一个TCP服务器套接字。 - 绑定套接字到特定的IP地址和端口号。 - 监听连接请求,等待客户端进行连接。 - 接受客户端连接请求。 - 接收客户端发送的FTP命令,解析命令,并执行相应的操作,如上传、下载、删除等。 - 处理客户端的连接断开请求。 2. FTP客户端的创建: - 创建一个TCP客户端套接字。 - 连接到FTP服务器的IP地址和端口号。 - 发送FTP命令给服务器端,如登录、上传、下载等。 - 接收服务器端发送的响应信息,并进行相应的处理。 - 处理与服务器的连接断开请求。 3. FTP协议的实现: - 根据FTP协议规定,实现FTP命令的解析与执行。 - 使用TCP协议进行数据的传输,如下载文件时,通过TCP连接发送文件的内容。 - 对于大文件的传输,可以分割成多个小块进行传输,并进行相应的校验,确保数据的完整性。 - 处理用户名和密码的验证,以及权限的控制。 4. 安全性的考虑: - 使用SSL/TLS协议对网络传输进行加密,确保数据的安全性。 - 对于文件传输过程中的敏感信息,如用户名和密码,使用加密算法进行保护。 通过以上步骤,可以实现基本的C语言FTP客户端和服务器。这些代码通常需要结合使用C语言的网络编程库,如Socket编程库,来实现网络连接和数据传输的功能。此外,还可以根据需求对代码进行优化和扩展,如支持断点续传、多线程下载等功能。 ### 回答3: C语言可以用于创建FTP(文件传输协议)客户端和服务器。FTP是一种用于在网络上传输文件的标准协议。下面将分别介绍如何使用C语言创建FTP客户端和服务器。 要创建FTP客户端,我们可以使用C语言的套接字编程。套接字是一种用于实现网络通信的接口。首先,我们需要创建一个套接字来建立与服务器的连接。然后,我们可以使用C语言的相关函数来实现FTP的各种命令,如登录、上传文件、下载文件等。最后,通过发送和接收套接字上的数据来实现服务器的通信。 要创建FTP服务器,我们同样可以使用C语言的套接字编程。首先,我们需要创建一个套接字并绑定到一个特定的端口上,以便客户端可以连接到服务器。然后,我们可以使用C语言的相关函数来实现FTP的各种命令的处理,如处理登录请求、处理文件上传请求、处理文件下载请求等。最后,通过发送和接收套接字上的数据来实现与客户端的通信。 在创建FTP客户端和服务器时,我们需要注意网络编程的一些基本概念和技术,如IP地址和端口号的使用、套接字的创建和使用、数据的发送和接收等。此外,还需要了解FTP协议的基本工作原理和相关命令的用法。 总的来说,使用C语言可以很好地实现FTP客户端和服务器的创建。但是,这需要一定的网络编程和C语言编程的知识和经验。对于初学者来说,可能需要更多的学习和实践才能熟练掌握。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值