自己动手写ftp服务器4(上传下载)
Tofloor
poster avatar
zhqh100
deepin
2014-05-02 07:24
Author
LIST已经实现了,后面实现ftp服务器的最核心功能——上传下载!
(当然,最大的bug是不支持中文,暂时也没考虑去解决此问题)

开发语言:C语言
操作系统:LinuxDeepin 2013
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. #include
  10. #include
  11. #include
  12. #include
  13. #include
  14. #define bool unsigned short
  15. typedef struct cmd_opts {
  16.         bool daemonize;
  17.         bool listen_any;
  18.         char *listen_addr;
  19.         int port;
  20.         int max_conn;
  21.         int userid;
  22.         char *chrootdir;
  23. }cmd_opts;
  24. //格式化字符串line
  25. static int write_file(char *line,const char *mode,int num,const char *user,const char * group,int size,const char *date,const char*fl_name) {
  26.         sprintf(line,"%s %3d %-4s %-4s %8d %12s %s\r\n",mode,num,user,group,size,date,fl_name);
  27.         return 0;
  28. }
  29. //判断字符是否是以某指令开始
  30. int strBeginWith(char *str,char *substr){
  31.         while(*str && *substr && *str==*substr){
  32.                 str++;
  33.                 substr++;
  34.         }
  35.         if(*substr=='\0')
  36.                 return 1;
  37.         else
  38.                 return 0;
  39. }
  40. //字符串转int
  41. int toint(char *str) {
  42.         if (strlen(str) < 1)
  43.         {
  44.                 return -1;
  45.         }
  46.         int retVal = str[0] - '0';
  47.         int i = 1;
  48.         while(str[i] >= '0' && str[i] <= '9')
  49.         {
  50.                 retVal = retVal * 10 + str[i] - '0';
  51.                 i++;
  52.         }
  53.         return retVal;
  54. }
  55. //向客户端发送消息
  56. int send_repl(int send_sock,char *msg) {
  57.         printf("Send Message:%s\n", msg);
  58.         if (send(send_sock, msg, strlen(msg),0) < 0) {
  59.                 perror("send");
  60.                 close(send_sock);
  61.                 return -1;
  62.         }
  63.         return 0;
  64. }
  65. //创建数据传输的连接,用于处理"LIST","STOR","RETR"等请求
  66. int make_client_connection(int client_port,const char* client_addr) {
  67.    int sock=-1;
  68.    struct sockaddr_in servaddr;
  69.    servaddr.sin_family = AF_INET;
  70.    servaddr.sin_addr.s_addr = inet_addr(client_addr);
  71.    servaddr.sin_port = htons (client_port);
  72.    if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  73.       send_repl(sock,"425 Can't open data connection.\r\n");
  74.       perror("socket");
  75.       return -1;
  76.    }
  77.    int status = connect (sock, (struct sockaddr *)&servaddr, sizeof (servaddr));
  78.    if(status!=0) {
  79.       send_repl(sock,"425 Can't open data connection.\r\n");
  80.       perror("connect");
  81.       return -1;
  82.    }
  83.    return sock;
  84. }
  85. //zhqh100@gmail.com
  86. enum {
  87.         TRUE=1,
  88.         FALSE=0
  89. };
  90. int main(int argc,char *argv[])
  91. {
  92.         struct cmd_opts *opts= malloc(sizeof(struct cmd_opts));
  93.    //设置一些初始配置
  94.         opts->daemonize    = FALSE;
  95.         opts->listen_any   = TRUE;
  96.         opts->port         = 21;
  97.         opts->userid       = 0;
  98.         opts->chrootdir    = "./";
  99.         opts->max_conn     = 5;
  100.         opts->listen_addr  = NULL;
  101.         struct sockaddr_in servaddr;
  102.         memset((char *)&servaddr, 0, sizeof(servaddr));
  103.    //TCP协议
  104.         servaddr.sin_family = PF_INET;
  105.         servaddr.sin_addr.s_addr =  htonl(INADDR_ANY);
  106.         servaddr.sin_port = htons (opts->port);
  107.         int servaddr_len = sizeof(servaddr);
  108.         int sock;
  109.         if ((sock = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  110.                 perror("socket");
  111.                 return -1;
  112.         }
  113.         int flag = 1;
  114.         setsockopt(sock, SOL_SOCKET,SO_REUSEADDR,(char *) &flag, sizeof(int));
  115.        
  116.         setsockopt(sock, IPPROTO_TCP,TCP_NODELAY,(char *) &flag, sizeof(int));
  117.    //套接字绑定端口和地址
  118.         if(bind (sock, (struct sockaddr *)&servaddr, sizeof(servaddr))<0) {
  119.                 perror("bind");
  120.                 return -1;
  121.         }
  122.         if(listen(sock,opts->max_conn) <0) {
  123.                 perror("listen");
  124.                 return -1;
  125.         }
  126.         int connection;
  127.         struct stat *s_buff = malloc(sizeof(struct stat));
  128.         char mode[11]        = "----------";
  129.         char date[16];
  130.         struct passwd * pass_info = NULL;
  131.         struct group * group_info;
  132.         struct dirent *d_next;
  133.         char line[300];
  134.         DIR *dir;
  135.         //传输类型
  136.         int type;
  137.         //文件指针
  138.         int fpr;
  139.         //长度
  140.         int len;
  141.         //死循环等待客户端连接
  142.         while(1){
  143.                 printf("Begin to accept\n");
  144.                 if ((connection = accept(sock, (struct sockaddr *) &servaddr, &servaddr_len)) < 0) {
  145.                         perror("accept");
  146.                         return -1;
  147.                 }
  148.                 int pid = fork();
  149.                 char msg[]= "220 Service ready for new user.\r\n";
  150.                 char send_buff[1000];
  151.                 char read_buff[1000];
  152.                 char current_dir[1000];
  153.                 //客户端IP地址和端口号
  154.                 char client_addr[16];
  155.                 int client_port;
  156.                 //临时指针
  157.                 char *temp;
  158.                 int i;
  159.                 int client_fd;
  160.                 strcpy(current_dir,opts->chrootdir);
  161.                 //获取当前绝对路径
  162.                 getcwd(current_dir,sizeof(current_dir));
  163.                 len = strlen(msg);
  164.                 if(pid==0) {
  165.          //客户端连接后,服务器先主动发送200 Service ready
  166.                         if (send(connection, msg, len,0) < 0) {
  167.                                 perror("send");
  168.                                 close(connection);
  169.                                 return -1;
  170.                         }
  171.                         while(1){
  172.             //初始化读取buffer和发送buffer
  173.                                 memset(send_buff,0,sizeof(send_buff));
  174.                                 memset(read_buff,0,sizeof(read_buff));
  175.             //等待接收客户端指令
  176.                                 int recvbuff = recv(connection,read_buff,sizeof(read_buff),0);
  177.                                 if(recvbuff<1) {
  178.                                         return -1;
  179.                                 }
  180.                                 if(recvbuff==sizeof(read_buff)) {
  181.                                         return -2;
  182.                                 }
  183.                                 printf("Received:%s",read_buff);
  184.                                 if (strBeginWith(read_buff,"USER")){
  185.                                         //收到客户端发送的用户名,这里不处理用户名,均答复登陆成功
  186.                                         //注释代码也可用,只是这里修改后,发送的消息更精简了
  187.                                         //后面的人性化提示可以省略,但\r\n不可省略,经Filezilla和 CuteFTP客户端验证,必须把\r\n带上才可正常识别
  188.                                         // if (send_repl(connection, "331 Anonymous login okay, send your complete email as your password.\r\n") < 0)
  189.                                         if(send_repl(connection,"331 Anonymous\r\n") < 0)
  190.                                                 return -1;
  191.                                 }else if(strBeginWith(read_buff,"PASS")){
  192.                                         //可以看到ftp协议发送的密码均为明文发送
  193.                                         //这里不做密码校验,均答复已登录
  194.                                         // if (send_repl(connection, "230 User logged in, proceed.\r\n") < 0)
  195.                                         if (send_repl(connection, "230 User\r\n") < 0)
  196.                                                 return -1;
  197.                                 }else if (strBeginWith(read_buff,"SYST")){
  198.                                         //问操作系统类型
  199.                                         //下面UNIX的可替代选项参考 http://www.iana.org/assignments/operating-system-names/operating-system-names.xhtml
  200.                                         if (send_repl(connection, "215 UNIX\r\n") < 0)
  201.                                                 return -1;
  202.                                 }else if (strBeginWith(read_buff,"PWD")){
  203.                                         //当前路径
  204.                                         sprintf(send_buff,"257 %s\r\n",current_dir);
  205.                                         if (send_repl(connection, send_buff) < 0)
  206.                                                 return -1;
  207.                                 }else if (strBeginWith(read_buff,"PORT")){
  208.                                         //客户端发来的IP和端口号
  209.                                         char *temp = strtok(read_buff + 5,",");
  210.                                         strcpy(client_addr, temp);
  211.                                         for (i = 0; i < 3; ++i)
  212.                                         {
  213.                                                 strcat(client_addr, ".");
  214.                                                 temp = strtok(NULL, ",");
  215.                                                 strcat(client_addr, temp);
  216.                                         }
  217.                                         printf("client_addr = %s\n", client_addr);
  218.                                         temp = strtok(NULL, ",");
  219.                                         client_port = toint(temp);
  220.                                         temp = strtok(NULL, ",");
  221.                                         client_port = client_port * 256 + toint(temp);
  222.                                         printf("client_port = %d\n", client_port);
  223.                                         //回复传说中的200 OK
  224.                                         if (send_repl(connection, "200 \r\n") < 0)
  225.                                                 return -1;
  226.                                 }else if (strBeginWith(read_buff,"LIST")){
  227.                                         //列出当前路径的所有文件
  228.                                         client_fd = make_client_connection(client_port, client_addr);
  229.                                         if (client_fd != -1){
  230.                                                 dir = opendir(current_dir);
  231.                                                 if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
  232.                                                         return -1;
  233.                                                 while(1) {
  234.                                                         d_next = readdir(dir);
  235.                                                         if(d_next==NULL)
  236.                                                                 break;
  237.                                                         line[0]='\0';
  238.                                                         stat(d_next->d_name,s_buff);
  239.                      int b_mask = s_buff->st_mode & S_IFMT;
  240.                      //判断是文件还是文件夹
  241.                      if(b_mask == S_IFDIR) {
  242.                         mode[0]='d';
  243.                      } else if(b_mask == S_IFREG){
  244.                         mode[0]='-';
  245.                      } else {
  246.                         return FALSE;
  247.                      }
  248.                                                         mode[1] = (s_buff->st_mode & S_IRUSR)?'r':'-';
  249.                                                         mode[2] = (s_buff->st_mode & S_IWUSR)?'w':'-';
  250.                                                         mode[3] = (s_buff->st_mode & S_IXUSR)?'x':'-';
  251.                                                         mode[4] = (s_buff->st_mode & S_IRGRP)?'r':'-';
  252.                                                         mode[5] = (s_buff->st_mode & S_IWGRP)?'w':'-';
  253.                                                         mode[6] = (s_buff->st_mode & S_IXGRP)?'x':'-';
  254.                                                         mode[7] = (s_buff->st_mode & S_IROTH)?'r':'-';
  255.                                                         mode[8] = (s_buff->st_mode & S_IWOTH)?'w':'-';
  256.                                                         mode[9] = (s_buff->st_mode & S_IXOTH)?'x':'-';
  257.                                                         strftime(date,13,"%b %d %H:%M",localtime(&(s_buff->st_mtime)));
  258.                                                         pass_info = getpwuid(s_buff->st_uid);
  259.                                                         group_info = getgrgid(s_buff->st_gid);
  260.                                                         write_file(line,mode,s_buff->st_nlink,pass_info->pw_name,group_info->gr_name,s_buff->st_size,date,d_next->d_name);
  261.                                                         printf("line = %s\n", line);
  262.                                                         send(client_fd,line,strlen(line),0);
  263.                                                 }
  264.                                                 close(client_fd);
  265.                                                 if(send_repl(connection,"226 Closing data connection.\r\n"))
  266.                                                         return -1;
  267.                                                 closedir(dir);
  268.                                                 client_fd = -1;
  269.                                         }
  270.                                 }else if(strBeginWith(read_buff,"TYPE")){
  271.                                         switch(read_buff[5]){
  272.                                                 //type A Ascii文件流,type I,二进制文件流
  273.                                                 case 'I':
  274.                                                         if (send_repl(connection, "200 \r\n") < 0)
  275.                                                                 return -1;
  276.                                                         type = 1;break;
  277.                                                 case 'A':
  278.                                                         if (send_repl(connection, "200 \r\n") < 0)
  279.                                                                 return -1;
  280.                                                         type = 2;break;
  281.                                                 case 'L':printf("get TYPE L,need to handle the request");break;
  282.                                                 default: break;
  283.                                         }
  284.                                 }else if(strBeginWith(read_buff,"RETR")){
  285.                                         client_fd = make_client_connection(client_port,client_addr);
  286.                                         if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
  287.                                                         return -1;
  288.                                         temp = read_buff+5;//filename
  289.                                         //我原以为temp就是文件名,实际却是客户端发过来的数据会在可见字符后面跟一堆不可见字符,所以需要进行处理
  290.                                         //这里进行简单粗暴的处理,会造成无法接收中文文件的传输
  291.                                         i = 0;
  292.                                         while(1){
  293.                                                 if(temp[i] < ' ' | temp[i] > '~'){
  294.                                                         temp[i] = '\0';
  295.                                                         break;
  296.                                                 }
  297.                                                 i++;
  298.                                         }
  299.                                         fpr = open(temp,O_RDONLY);
  300.                                         if (fpr < 0)
  301.                                         {
  302.                                                 perror("open");
  303.                                                 return -1;
  304.                                         }
  305.                                         while(1){
  306.                                                 len = read(fpr,send_buff,sizeof(send_buff));
  307.                                                 if(len>0) {
  308.                                                         send(client_fd,send_buff,len,0);
  309.                                                 }
  310.                                                 else {
  311.                                                         break;
  312.                                                 }
  313.                                         }
  314.                                         close(fpr);
  315.                                         send_repl(connection,"226 Closing data connection.\r\n");
  316.                                         close(client_fd);
  317.                                 }else if(strBeginWith(read_buff,"STOR")){
  318.                                         client_fd = make_client_connection(client_port,client_addr);
  319.                                         if(send_repl(connection,"150 File status okay; about to open data connection.\r\n") < 0)
  320.                                                         return -1;
  321.                                         temp = read_buff+5;//filename
  322.                                         i = 0;
  323.                                         while(1){
  324.                                                 if(temp[i] < ' ' | temp[i] > '~'){
  325.                                                         temp[i] = '\0';
  326.                                                         break;
  327.                                                 }
  328.                                                 i++;
  329.                                         }
  330.                                         fpr = open(temp,O_WRONLY|O_CREAT,0644);
  331.                                         if (fpr < 0)
  332.                                         {
  333.                                                 perror(temp);
  334.                                                 return -1;
  335.                                         }
  336.                                         while(1){
  337.                                                 len = recv(client_fd,read_buff,sizeof(read_buff),0);
  338.                                                 if(len>0) {
  339.                                                         write(fpr,read_buff,len);
  340.                                                 }
  341.                                                 else {
  342.                                                         break;
  343.                                                 }
  344.                                         }
  345.                                         close(fpr);
  346.                                         send_repl(connection,"226 Closing data connection.\r\n");
  347.                                         close(client_fd);
  348.                                 }else{
  349.                                         //其他一律回复500,I don't know
  350.                                         if (send_repl(connection, "500 \r\n") < 0)
  351.                                                 return -1;
  352.                                 }
  353.                         }
  354.                 } else if(pid>0) {
  355.                         close(connection);
  356.                 }
  357.         }
  358.         return 0;
  359.        
  360. }
Copy the Code

可以上传一个大文件进行测试,如上传一个大的视频文件,如果上传后视频打开画面和本地画面一致,就证明功能实现正常。

不上传图片了
Reply Favorite View the author
All Replies

No replies yet