自己动手写ftp服务器3(像模像样)
Tofloor
poster avatar
zhqh100
deepin
2014-05-01 06:43
Author
接下来实现List功能,之后就可以看到文件夹下的列表了,也可以证明,这确实是一个像ftp服务器的一堆代码了

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

CuteFtp登陆看到如下信息:
QQ图片20140430222730.jpg
Reply Favorite View the author
All Replies
cxbii
deepin
2014-05-02 04:24
#1
支持,楼主是否考虑出个专题?
Reply View the author
zhqh100
deepin
2014-05-02 06:34
#2
支持,楼主是否考虑出个专题?

专题怎么出啊?
我这个再有两篇就很快完结了,不会很深入,接下来计划是《自己动手写http服务器》
Reply View the author