基于http的多进程并发文件服务器
Posted L的存在
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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 }
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 cpp►多线程