自己动手写ftp服务器5(到此为止)
Tofloor
poster avatar
zhqh100
deepin
2014-05-04 05:45
Author
实现了上传下载,我计划的功能也就实现了,本节稍微实现一下FTP无法缺少的删除和切换路径的功能,再进一步的功能如重命名、复制粘贴等功能各位可以直接看tinyftp的源代码。

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

ftp就先研究到这里,接下来开始学习http的源码,可能的话后面再发一些心得
Reply Favorite View the author
All Replies
yeser
deepin
2014-05-05 06:33
#1
虽一点不懂,但看看行数不多,觉得不难,呵呵!
Reply View the author
键盘机
deepin
2014-07-29 22:52
#2
马克一下
Reply View the author