欢迎来到贱贱的博客

扩大
缩小

基于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] = '\0';
 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\r\nServer:Test http server\r\nConnection: close\r\n\r\n<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: \n%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\r\nServer: Test http server\r\nConnection: keep-alive\r\nContent-type: application/*\r\nContent-Length:%d\r\n\r\n", 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\r\nServer:Test http server\r\nConnection:close\r\n\r\n<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>\n",
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>\n");
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>\n");
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\r\nServer:Test http server\r\nConnection: close\r\n\r\n<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\n",
 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\"\n", 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,如果指定就写上自己制定的端口)

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

posted on 2019-04-09 10:38  L的存在  阅读(846)  评论(0编辑  收藏  举报

导航