• 基于http的多进程并发文件服务器


    1 可以掌握的知识点

    (1) 线上部署时的守护应用

    (2) 常规的文件操作,配置文件读取

    (3) 网络编程,端口复用等文件

    (4) 多进程知识

    2 代码注释如下

    test_httpd.h

      1 #include <pwd.h>
      2 #include <grp.h>
      3 #include <net/if.h>
      4 #include <sys/ioctl.h>
      5 #include <sys/syslog.h>
      6 #include <stdarg.h>
      7 #include <errno.h>
      8 #include <stdio.h>
      9 #include <fcntl.h>
     10 #include <unistd.h>
     11 #include <string.h>
     12 #include <time.h>
     13 #include <sys/types.h>
     14 #include <sys/stat.h>
     15 #include <dirent.h>
     16 #include <errno.h>
     17 #include <netinet/in.h>
     18 #include <sys/socket.h>
     19 #include <resolv.h>
     20 #include <arpa/inet.h>
     21 #include <stdlib.h>
     22 #include <signal.h>
     23 #include <getopt.h>
     24 #include <net/if.h>
     25 #include <sys/ioctl.h>
     26 
     27 #define MAXBUF        1024
     28 #define MAXPATH        128
     29 
     30 char buffer[MAXBUF + 1];
     31 char ip[128];//存储字符串形式的Ip地址
     32 char port[128];//存储字符串形式的端口
     33 char back[128];//listen队列大小
     34 char home_dir[128];//浏览主目录
     35 
     36 
     37 void wrtinfomsg(char *msg)        
     38 {  
     39     syslog(LOG_INFO,"%s",msg);
     40 }
     41 
     42 
     43 //读取配置文件
     44 int get_arg (char *cmd)
     45 {
     46 
     47         FILE* fp;
     48         char buffer[1024];
     49         size_t bytes_read;
     50         char* match;
     51         fp = fopen ("/etc/test_httpd.conf", "r");
     52         bytes_read = fread (buffer, 1, sizeof (buffer), fp);
     53         fclose (fp);
     54 
     55         if (bytes_read == 0 || bytes_read == sizeof (buffer))
     56                 return 0;
     57         buffer[bytes_read] = '';
     58         
     59         //根据配置文件key得到value
     60         if(!strncmp(cmd,"home_dir",8))
     61         {
     62                 match = strstr (buffer, "home_dir=");
     63                 if (match == NULL)
     64                         return 0;
     65                 bytes_read=sscanf(match,"home_dir=%s",home_dir);
     66                 return bytes_read;
     67         }
     68 
     69         else if(!strncmp(cmd,"port",4))
     70         {
     71                 match = strstr (buffer, "port=");
     72                 if (match == NULL)
     73                         return 0;
     74                 bytes_read=sscanf(match,"port=%s",port);
     75                 return bytes_read;
     76         }
     77 
     78         else if(!strncmp(cmd,"ip",2))
     79         {
     80                 match = strstr (buffer, "ip=");
     81                 if (match == NULL)
     82                         return 0;
     83                 bytes_read=sscanf(match,"ip=%s",ip);
     84                 return bytes_read;
     85         }
     86         else if(!strncmp(cmd,"back",4))
     87         {
     88                 match = strstr (buffer, "back=");
     89                 if (match == NULL)
     90                         return 0;
     91                 bytes_read=sscanf(match,"back=%s",back);
     92                 return bytes_read;
     93         }
     94      else
     95             return 0;
     96 }
     97 
     98 //文件类型的判断
     99 char file_type(mode_t st_mode)
    100 {
    101     if ((st_mode & S_IFMT) == S_IFSOCK)
    102         return 's';
    103     else if ((st_mode & S_IFMT) == S_IFLNK)
    104         return 'l';
    105     else if ((st_mode & S_IFMT) == S_IFREG)
    106         return '-';
    107     else if ((st_mode & S_IFMT) == S_IFBLK)
    108         return 'b';
    109     else if ((st_mode & S_IFMT) == S_IFCHR)
    110         return 'c';
    111     else if ((st_mode & S_IFMT) == S_IFIFO)
    112         return 'p';
    113     else
    114         return 'd';
    115 }
    116 
    117 //search the up-path of dirpath
    118 char *dir_up(char *dirpath)
    119 {
    120     static char Path[MAXPATH];
    121     int len;
    122 
    123     strcpy(Path, dirpath);
    124     len = strlen(Path);
    125     if (len > 1 && Path[len - 1] == '/')
    126         len--;
    127     while (Path[len - 1] != '/' && len > 1)
    128         len--;
    129     Path[len] = 0;
    130     return Path;
    131 }
    132 
    133 //如果路径是文件 就将文件的内容发送过去 如果是目录则列出目录文件
    134 void GiveResponse(FILE * client_sock, char *Path)
    135 {
    136     struct dirent *dirent;
    137     struct stat info;
    138     char Filename[MAXPATH];
    139     DIR *dir;
    140     int fd, len, ret;
    141     char *p, *realPath, *realFilename, *nport;
    142 
    143     struct passwd *p_passwd;
    144     struct group *p_group;
    145     char *p_time;    
    146 
    147     //get th dir or file
    148     len = strlen(home_dir) + strlen(Path) + 1;
    149     realPath = malloc(len + 1);
    150     bzero(realPath, len + 1);
    151     sprintf(realPath, "%s/%s", home_dir, Path);//获取文件的绝对路径
    152 
    153     //get port
    154     len = strlen(port) + 1;
    155     nport = malloc(len + 1);
    156     bzero(nport, len + 1);
    157     sprintf(nport, ":%s", port);//存储端口信息
    158 
    159 
    160     
    161     //获取文件属性
    162     if (stat(realPath, &info)) {
    163         //获取失败   输出这样信息 这信息格式满足http协议
    164         fprintf(client_sock,
    165                 "HTTP/1.1 200 OK
    Server:Test http server
    Connection: close
    
    <html><head><title>%d - %s</title></head>"
    166                 "<body><font size=+4>Linux HTTP server</font><br><hr width="100%%"><br><center>"
    167                 "<table border cols=3 width="100%%">", errno,
    168                 strerror(errno));
    169         fprintf(client_sock,
    170                 "</table><font color="CC0000" size=+2> connect to administrator, error code is: 
    %s %s</font></body></html>",
    171                 Path, strerror(errno));
    172         goto out;
    173     }
    174 
    175     //如果是文件则下载
    176     if (S_ISREG(info.st_mode)) 
    177     {
    178         fd = open(realPath, O_RDONLY);
    179         len = lseek(fd, 0, SEEK_END);
    180         p = (char *) malloc(len + 1);
    181         bzero(p, len + 1);
    182         lseek(fd, 0, SEEK_SET);
    183         //这里是一次性读取 小文件
    184         ret = read(fd, p, len);
    185         close(fd);
    186         fprintf(client_sock,
    187                 "HTTP/1.1 200 OK
    Server: Test http server
    Connection: keep-alive
    Content-type: application/*
    Content-Length:%d
    
    ", len);
    188        
    189         fwrite(p, len, 1, client_sock);
    190         free(p);
    191     } 
    192     else if (S_ISDIR(info.st_mode)) 
    193     {
    194 
    195     //列出目录
    196         dir = opendir(realPath);
    197         fprintf(client_sock,
    198                 "HTTP/1.1 200 OK
    Server:Test http server
    Connection:close
    
    <html><head><title>%s</title></head>"
    199                 "<body><font size=+4>Linux HTTP server file</font><br><hr width="100%%"><br><center>"
    200                 "<table border cols=3 width="100%%">", Path);
    201         fprintf(client_sock,
    202                 "<caption><font size=+3> Directory %s</font></caption>
    ",
    203                 Path);
    204         fprintf(client_sock,
    205                 "<tr><td>name</td><td>type</td><td>owner</td><td>group</td><td>size</td><td>modify time</td></tr>
    ");
    206         if (dir == NULL) {
    207             fprintf(client_sock, "</table><font color="CC0000" size=+2>%s</font></body></html>",
    208                     strerror(errno));
    209             return;
    210         }
    211         while ((dirent = readdir(dir)) != NULL) 
    212         {
    213             if (strcmp(Path, "/") == 0)
    214                 sprintf(Filename, "/%s", dirent->d_name);
    215             else
    216                 sprintf(Filename, "%s/%s", Path, dirent->d_name);
    217             if(dirent->d_name[0]=='.')
    218                 continue;
    219             fprintf(client_sock, "<tr>");
    220             len = strlen(home_dir) + strlen(Filename) + 1;
    221             realFilename = malloc(len + 1);
    222             bzero(realFilename, len + 1);
    223             sprintf(realFilename, "%s/%s", home_dir, Filename);
    224             if (stat(realFilename, &info) == 0) 
    225             {
    226                 if (strcmp(dirent->d_name, "..") == 0)
    227                     fprintf(client_sock, "<td><a href="http://%s%s%s">(parent)</a></td>",
    228                             ip, atoi(port) == 80 ? "" : nport,dir_up(Path));
    229                 else
    230                     fprintf(client_sock, "<td><a href="http://%s%s%s">%s</a></td>",
    231                             ip, atoi(port) == 80 ? "" : nport, Filename, dirent->d_name);
    232                
    233 
    234 
    235                   p_time = ctime(&info.st_mtime);//获取文件修改时间
    236                    p_passwd = getpwuid(info.st_uid);    //获取文件拥有着
    237                    p_group = getgrgid(info.st_gid);//获取文件拥有者组
    238 
    239                   fprintf(client_sock, "<td>%c</td>", file_type(info.st_mode));
    240                    fprintf(client_sock, "<td>%s</td>", p_passwd->pw_name);
    241                   fprintf(client_sock, "<td>%s</td>", p_group->gr_name);
    242                   fprintf(client_sock, "<td>%d</td>", info.st_size);
    243                 fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime));
    244             }
    245             fprintf(client_sock, "</tr>
    ");
    246             free(realFilename);
    247         }
    248         fprintf(client_sock, "</table></center></body></html>");
    249     } else {
    250       //if others,forbid access
    251         fprintf(client_sock,
    252                 "HTTP/1.1 200 OK
    Server:Test http server
    Connection: close
    
    <html><head><title>permission denied</title></head>"
    253                 "<body><font size=+4>Linux HTTP server</font><br><hr width="100%%"><br><center>"
    254                 "<table border cols=3 width="100%%">");
    255         fprintf(client_sock,
    256                 "</table><font color="CC0000" size=+2> you access resource '%s' forbid to access,communicate with the admintor </font></body></html>",
    257                 Path);
    258     }
    259   out:
    260     free(realPath);
    261     free(nport);
    262 }
    263 
    264 //守护
    265 void init_daemon(const char *pname, int facility)
    266 {
    267     int pid; 
    268     int i;
    269     signal(SIGTTOU,SIG_IGN); 
    270     signal(SIGTTIN,SIG_IGN); 
    271     signal(SIGTSTP,SIG_IGN); 
    272     signal(SIGHUP ,SIG_IGN);
    273     if(pid=fork()) 
    274         exit(EXIT_SUCCESS); 
    275     else if(pid< 0) 
    276     {
    277         perror("fork");
    278         exit(EXIT_FAILURE);
    279     }
    280     setsid(); 
    281     if(pid=fork()) 
    282         exit(EXIT_SUCCESS); 
    283     else if(pid< 0) 
    284     {
    285         perror("fork");
    286         exit(EXIT_FAILURE);
    287     }  
    288     for(i=0;i< NOFILE;++i)
    289         close(i);
    290     chdir("/tmp"); 
    291     umask(0);  
    292     signal(SIGCHLD,SIG_IGN);
    293     openlog(pname, LOG_PID, facility);
    294     return; 
    295 } 
    296 
    297 //如果配置文件不指定ip  那么默认读取eth0
    298 int get_addr(char *str)
    299 {
    300         int inet_sock;
    301         struct ifreq ifr;
    302         inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
    303         strcpy(ifr.ifr_name, str);
    304         if (ioctl(inet_sock, SIOCGIFADDR, &ifr) < 0)
    305         {
    306             wrtinfomsg("bind");
    307             exit(EXIT_FAILURE);
    308         }
    309         sprintf(ip,"%s", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
    310 }
    View Code

    test_httpd.c

      1 #include"test_httpd.h"
      2 
      3 int main(int argc, char **argv)
      4 {
      5     struct sockaddr_in addr;
      6     int sock_fd, addrlen;
      7     
      8     init_daemon(argv[0],LOG_INFO);
      9     if(get_arg("home_dir")==0)
     10     {
     11         sprintf(home_dir,"%s","/tmp");
     12     }
     13     if(get_arg("ip")==0)
     14     {
     15         get_addr("eth0");
     16     }
     17     if(get_arg("port")==0)
     18     {
     19         sprintf(port,"%s","80");
     20     }
     21     if(get_arg("back")==0)
     22     {
     23         sprintf(back,"%s","5");
     24     }
     25 
     26     if ((sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) 
     27     {
     28         wrtinfomsg("socket()");
     29         exit(EXIT_FAILURE);
     30     }
     31     addrlen = 1;
     32     //端口复用 选项SO_REUSEADDR
     33     setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen, sizeof(addrlen));
     34 
     35     addr.sin_family = AF_INET;
     36     addr.sin_port = htons(atoi(port));
     37     addr.sin_addr.s_addr = inet_addr(ip);
     38     addrlen = sizeof(struct sockaddr_in);
     39     if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0)
     40     {
     41         wrtinfomsg("bind");
     42         exit(EXIT_FAILURE);
     43     }
     44     if (listen(sock_fd, atoi(back)) < 0)    
     45     {
     46         wrtinfomsg("listen");
     47         exit(EXIT_FAILURE);
     48     }
     49     while (1) 
     50     {
     51         int len;
     52         int new_fd;
     53         addrlen = sizeof(struct sockaddr_in);
     54 
     55         new_fd = accept(sock_fd, (struct sockaddr *) &addr, &addrlen);
     56         if (new_fd < 0)
     57         {
     58             wrtinfomsg("accept");
     59             exit(EXIT_FAILURE);
     60         }
     61 
     62         bzero(buffer, MAXBUF + 1);
     63         sprintf(buffer, "connect come from: %s:%d
    ",
     64                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
     65         wrtinfomsg(buffer);
     66 
     67           //fork a new process to deal with the connect ,the parent continue wait for new connect
     68        pid_t pid;  
     69        if((pid=fork())==-1)
     70        {
     71             wrtinfomsg("fork");
     72             exit(EXIT_FAILURE);
     73         }
     74        if (pid==0) 
     75        {
     76             close(sock_fd);
     77             bzero(buffer, MAXBUF + 1);
     78             if ((len = recv(new_fd, buffer, MAXBUF, 0)) > 0) 
     79             {
     80                 FILE *ClientFP = fdopen(new_fd, "w");
     81                 if (ClientFP == NULL) 
     82                 {
     83                     wrtinfomsg("fdopen");
     84                     exit(EXIT_FAILURE);
     85                 } 
     86                 else 
     87                 {
     88                     char Req[MAXPATH + 1] = "";
     89                     sscanf(buffer, "GET %s HTTP", Req);
     90                     bzero(buffer, MAXBUF + 1);
     91                     sprintf(buffer, "Reuquest get the file: "%s"
    ", Req);
     92                     wrtinfomsg(buffer);
     93                     GiveResponse(ClientFP, Req);
     94                     fclose(ClientFP);
     95                 }
     96             }
     97             exit(EXIT_SUCCESS);
     98         }
     99         else
    100         {
    101             close(new_fd);
    102             continue;
    103         }
    104     }
    105     close(sock_fd);
    106     return 0;
    107 } 
    View Code

    makefile

    test_httpd: test_httpd.c test_httpd.h
        gcc -o test_httpd test_httpd.c test_httpd.h
    
    
    install:
        cp test_httpd.conf /etc/test_httpd.conf
        cp test_httpd /usr/bin/test_httpd
    
    clean:
        rm test_httpd
    
    uninstall:    
        rm /usr/bin/test_httpd
    View Code

    test_httpd.conf

    1 home_dir=/var
    2 port=9999
    3 back=5
    4 ip=写上自己主机上面的IP或者不写
    View Code

    3 运行方法

    (1)make

    (2)make install 这个时候会把程序添加在PATH路径下

    (3)查看端口是否打开

    (4)./test_httpd

    (5)浏览器访问 htpp://你的ip:port(默认位80,如果指定就写上自己制定的端口)

     后面学习线程池的文件服务器

  • 相关阅读:
    SpringMVC将表单对象序列化成Json字符串提交,以List接收
    Spring boot下添加filter
    如何将查出的日期Data类型以Json格式输出到前端
    ajax传递给后台数组参数方式
    Spring boot + Gradle + Eclipse打war包发布总结
    Spring-data-jpa详解
    SpringMVC配置过程中出现的问题!
    spring 集成shiro 之 自定义过滤器
    完全跨域的单点登录
    Java8 Lambda表达式
  • 原文地址:https://www.cnblogs.com/lanjianhappy/p/10675459.html
Copyright © 2020-2023  润新知