基于http的多进程并发文件服务器
1 可以掌握的知识点
(1) 线上部署时的守护应用
(2) 常规的文件操作,配置文件读取
(3) 网络编程,端口复用等文件
(4) 多进程知识
2 代码注释如下
test_httpd.h
#include <pwd.h>
#include <grp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <stdarg.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>
#include <net/if.h>
#include <sys/ioctl.h> #define MAXBUF 1024
#define MAXPATH 128 char buffer[MAXBUF + ];
char ip[];//存储字符串形式的Ip地址
char port[];//存储字符串形式的端口
char back[];//listen队列大小
char home_dir[];//浏览主目录 void wrtinfomsg(char *msg)
{
syslog(LOG_INFO,"%s",msg);
} //读取配置文件
int get_arg (char *cmd)
{ FILE* fp;
char buffer[];
size_t bytes_read;
char* match;
fp = fopen ("/etc/test_httpd.conf", "r");
bytes_read = fread (buffer, , sizeof (buffer), fp);
fclose (fp); if (bytes_read == || bytes_read == sizeof (buffer))
return ;
buffer[bytes_read] = '\0'; //根据配置文件key得到value
if(!strncmp(cmd,"home_dir",))
{
match = strstr (buffer, "home_dir=");
if (match == NULL)
return ;
bytes_read=sscanf(match,"home_dir=%s",home_dir);
return bytes_read;
} else if(!strncmp(cmd,"port",))
{
match = strstr (buffer, "port=");
if (match == NULL)
return ;
bytes_read=sscanf(match,"port=%s",port);
return bytes_read;
} else if(!strncmp(cmd,"ip",))
{
match = strstr (buffer, "ip=");
if (match == NULL)
return ;
bytes_read=sscanf(match,"ip=%s",ip);
return bytes_read;
}
else if(!strncmp(cmd,"back",))
{
match = strstr (buffer, "back=");
if (match == NULL)
return ;
bytes_read=sscanf(match,"back=%s",back);
return bytes_read;
}
else
return ;
} //文件类型的判断
char file_type(mode_t st_mode)
{
if ((st_mode & S_IFMT) == S_IFSOCK)
return 's';
else if ((st_mode & S_IFMT) == S_IFLNK)
return 'l';
else if ((st_mode & S_IFMT) == S_IFREG)
return '-';
else if ((st_mode & S_IFMT) == S_IFBLK)
return 'b';
else if ((st_mode & S_IFMT) == S_IFCHR)
return 'c';
else if ((st_mode & S_IFMT) == S_IFIFO)
return 'p';
else
return 'd';
} //search the up-path of dirpath
char *dir_up(char *dirpath)
{
static char Path[MAXPATH];
int len; strcpy(Path, dirpath);
len = strlen(Path);
if (len > && Path[len - ] == '/')
len--;
while (Path[len - ] != '/' && len > )
len--;
Path[len] = ;
return Path;
} //如果路径是文件 就将文件的内容发送过去 如果是目录则列出目录文件
void GiveResponse(FILE * client_sock, char *Path)
{
struct dirent *dirent;
struct stat info;
char Filename[MAXPATH];
DIR *dir;
int fd, len, ret;
char *p, *realPath, *realFilename, *nport; struct passwd *p_passwd;
struct group *p_group;
char *p_time; //get th dir or file
len = strlen(home_dir) + strlen(Path) + ;
realPath = malloc(len + );
bzero(realPath, len + );
sprintf(realPath, "%s/%s", home_dir, Path);//获取文件的绝对路径 //get port
len = strlen(port) + ;
nport = malloc(len + );
bzero(nport, len + );
sprintf(nport, ":%s", port);//存储端口信息 //获取文件属性
if (stat(realPath, &info)) {
//获取失败 输出这样信息 这信息格式满足http协议
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer:Test http server\r\nConnection: close\r\n\r\n<html><head><title>%d - %s</title></head>"
"<body><font size=+4>Linux HTTP server</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">", errno,
strerror(errno));
fprintf(client_sock,
"</table><font color=\"CC0000\" size=+2> connect to administrator, error code is: \n%s %s</font></body></html>",
Path, strerror(errno));
goto out;
} //如果是文件则下载
if (S_ISREG(info.st_mode))
{
fd = open(realPath, O_RDONLY);
len = lseek(fd, , SEEK_END);
p = (char *) malloc(len + );
bzero(p, len + );
lseek(fd, , SEEK_SET);
//这里是一次性读取 小文件
ret = read(fd, p, len);
close(fd);
fprintf(client_sock,
"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); fwrite(p, len, , client_sock);
free(p);
}
else if (S_ISDIR(info.st_mode))
{ //列出目录
dir = opendir(realPath);
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer:Test http server\r\nConnection:close\r\n\r\n<html><head><title>%s</title></head>"
"<body><font size=+4>Linux HTTP server file</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">", Path);
fprintf(client_sock,
"<caption><font size=+3> Directory %s</font></caption>\n",
Path);
fprintf(client_sock,
"<tr><td>name</td><td>type</td><td>owner</td><td>group</td><td>size</td><td>modify time</td></tr>\n");
if (dir == NULL) {
fprintf(client_sock, "</table><font color=\"CC0000\" size=+2>%s</font></body></html>",
strerror(errno));
return;
}
while ((dirent = readdir(dir)) != NULL)
{
if (strcmp(Path, "/") == )
sprintf(Filename, "/%s", dirent->d_name);
else
sprintf(Filename, "%s/%s", Path, dirent->d_name);
if(dirent->d_name[]=='.')
continue;
fprintf(client_sock, "<tr>");
len = strlen(home_dir) + strlen(Filename) + ;
realFilename = malloc(len + );
bzero(realFilename, len + );
sprintf(realFilename, "%s/%s", home_dir, Filename);
if (stat(realFilename, &info) == )
{
if (strcmp(dirent->d_name, "..") == )
fprintf(client_sock, "<td><a href=\"http://%s%s%s\">(parent)</a></td>",
ip, atoi(port) == ? "" : nport,dir_up(Path));
else
fprintf(client_sock, "<td><a href=\"http://%s%s%s\">%s</a></td>",
ip, atoi(port) == ? "" : nport, Filename, dirent->d_name); p_time = ctime(&info.st_mtime);//获取文件修改时间
p_passwd = getpwuid(info.st_uid); //获取文件拥有着
p_group = getgrgid(info.st_gid);//获取文件拥有者组 fprintf(client_sock, "<td>%c</td>", file_type(info.st_mode));
fprintf(client_sock, "<td>%s</td>", p_passwd->pw_name);
fprintf(client_sock, "<td>%s</td>", p_group->gr_name);
fprintf(client_sock, "<td>%d</td>", info.st_size);
fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime));
}
fprintf(client_sock, "</tr>\n");
free(realFilename);
}
fprintf(client_sock, "</table></center></body></html>");
} else {
//if others,forbid access
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer:Test http server\r\nConnection: close\r\n\r\n<html><head><title>permission denied</title></head>"
"<body><font size=+4>Linux HTTP server</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">");
fprintf(client_sock,
"</table><font color=\"CC0000\" size=+2> you access resource '%s' forbid to access,communicate with the admintor </font></body></html>",
Path);
}
out:
free(realPath);
free(nport);
} //守护
void init_daemon(const char *pname, int facility)
{
int pid;
int i;
signal(SIGTTOU,SIG_IGN);
signal(SIGTTIN,SIG_IGN);
signal(SIGTSTP,SIG_IGN);
signal(SIGHUP ,SIG_IGN);
if(pid=fork())
exit(EXIT_SUCCESS);
else if(pid< )
{
perror("fork");
exit(EXIT_FAILURE);
}
setsid();
if(pid=fork())
exit(EXIT_SUCCESS);
else if(pid< )
{
perror("fork");
exit(EXIT_FAILURE);
}
for(i=;i< NOFILE;++i)
close(i);
chdir("/tmp");
umask();
signal(SIGCHLD,SIG_IGN);
openlog(pname, LOG_PID, facility);
return;
} //如果配置文件不指定ip 那么默认读取eth0
int get_addr(char *str)
{
int inet_sock;
struct ifreq ifr;
inet_sock = socket(AF_INET, SOCK_DGRAM, );
strcpy(ifr.ifr_name, str);
if (ioctl(inet_sock, SIOCGIFADDR, &ifr) < )
{
wrtinfomsg("bind");
exit(EXIT_FAILURE);
}
sprintf(ip,"%s", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
}
test_httpd.c
#include"test_httpd.h" int main(int argc, char **argv)
{
struct sockaddr_in addr;
int sock_fd, addrlen; init_daemon(argv[],LOG_INFO);
if(get_arg("home_dir")==)
{
sprintf(home_dir,"%s","/tmp");
}
if(get_arg("ip")==)
{
get_addr("eth0");
}
if(get_arg("port")==)
{
sprintf(port,"%s","");
}
if(get_arg("back")==)
{
sprintf(back,"%s","");
} if ((sock_fd = socket(PF_INET, SOCK_STREAM, )) < )
{
wrtinfomsg("socket()");
exit(EXIT_FAILURE);
}
addrlen = ;
//端口复用 选项SO_REUSEADDR
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &addrlen, sizeof(addrlen)); addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = inet_addr(ip);
addrlen = sizeof(struct sockaddr_in);
if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < )
{
wrtinfomsg("bind");
exit(EXIT_FAILURE);
}
if (listen(sock_fd, atoi(back)) < )
{
wrtinfomsg("listen");
exit(EXIT_FAILURE);
}
while ()
{
int len;
int new_fd;
addrlen = sizeof(struct sockaddr_in); new_fd = accept(sock_fd, (struct sockaddr *) &addr, &addrlen);
if (new_fd < )
{
wrtinfomsg("accept");
exit(EXIT_FAILURE);
} bzero(buffer, MAXBUF + );
sprintf(buffer, "connect come from: %s:%d\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
wrtinfomsg(buffer); //fork a new process to deal with the connect ,the parent continue wait for new connect
pid_t pid;
if((pid=fork())==-)
{
wrtinfomsg("fork");
exit(EXIT_FAILURE);
}
if (pid==)
{
close(sock_fd);
bzero(buffer, MAXBUF + );
if ((len = recv(new_fd, buffer, MAXBUF, )) > )
{
FILE *ClientFP = fdopen(new_fd, "w");
if (ClientFP == NULL)
{
wrtinfomsg("fdopen");
exit(EXIT_FAILURE);
}
else
{
char Req[MAXPATH + ] = "";
sscanf(buffer, "GET %s HTTP", Req);
bzero(buffer, MAXBUF + );
sprintf(buffer, "Reuquest get the file: \"%s\"\n", Req);
wrtinfomsg(buffer);
GiveResponse(ClientFP, Req);
fclose(ClientFP);
}
}
exit(EXIT_SUCCESS);
}
else
{
close(new_fd);
continue;
}
}
close(sock_fd);
return ;
}
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
test_httpd.conf
home_dir=/var
port=
back=
ip=写上自己主机上面的IP或者不写
3 运行方法
(1)make
(2)make install 这个时候会把程序添加在PATH路径下
(3)查看端口是否打开
(4)./test_httpd
(5)浏览器访问 htpp://你的ip:port(默认位80,如果指定就写上自己制定的端口)
后面学习线程池的文件服务器
基于http的多进程并发文件服务器的更多相关文章
- python多进程并发和多线程并发和协程
为什么需要并发编程? 如果程序中包含I/O操作,程序会有很高的延迟,CPU会处于等待状态,这样会浪费系统资源,浪费时间 1.Python的并发编程分为多进程并发和多线程并发 多进程并发:运行多个独立的 ...
- Appium+python自动化(三十六)- 士兵突击许三多 - 多个appium服务启动,多个设备启动,多进程并发启动设备-并发测试 - 上(超详解)
简介 前面课程只是启动了单个appium服务,只能控制单台设备.如果需要针对多台设备测试那么该如何处理?而且发现群里的小伙伴们也在时不时地在讨论这个问题,想知道怎么实现的,于是宏哥就决定写一片这样的文 ...
- 【redis】基于redis实现分布式并发锁
基于redis实现分布式并发锁(注解实现) 说明 前提, 应用服务是分布式或多服务, 而这些"多"有共同的"redis"; (2017-12-04) 笑哭, 写 ...
- Linux Shell多进程并发以及并发数控制
1. 基础知识准备 1.1. linux后台进程 Unix是一个多任务系统,允许多用户同时运行多个程序.shell的元字符&提供了在后台运行不需要键盘输入的程序的方法.输入命令后,其后紧跟&a ...
- Python多进程并发(multiprocessing)用法实例详解
http://www.jb51.net/article/67116.htm 本文实例讲述了Python多进程并发(multiprocessing)用法.分享给大家供大家参考.具体分析如下: 由于Pyt ...
- 基于RTKLIB构建高并发通信测试工具
1. RTKLIB基础动态库生成 RTKLIB是全球导航卫星系统GNSS(global navigation satellite system)的标准&精密定位开源程序包,由日本东京海洋大学的 ...
- python进阶(一) 多进程并发机制
python多进程并发机制: 这里使用了multprocessing.Pool进程池,来动态增加进程 #coding=utf-8 from multiprocessing import Pool im ...
- python 多进程并发与多线程并发
本文对python支持的几种并发方式进行简单的总结. Python支持的并发分为多线程并发与多进程并发(异步IO本文不涉及).概念上来说,多进程并发即运行多个独立的程序,优势在于并发处理的任务都由操作 ...
- Appium+python自动化(三十七)- 士兵突击许三多 - 多个appium服务启动,多个设备启动,多进程并发启动设备-并发测试 - 下(超详解)
简介 接着上一篇继续看一下如何并发测试以及并发测试的过程中,可能遇到的问题,在这里宏哥把宏哥遇到的和小伙伴或者童鞋们,一起分享一下. Appium端口检测 问题思考 经过前面学习,我们已经能够使用py ...
随机推荐
- python 3 递归调用与二分法
递归调用与二分法 1.递归调用 递归调用:在调用一个函数的过程中,直接或间接地调用了函数本身. 示例: def age(n): if n == 1: return 18 # 结束条件 return a ...
- 【Flask】ORM模型创建及数据库映射
1. 用 declarative_base 根据 engine 创建一个ORM基类.2. 用 Base 类作为基类来写自己的ORM类.要定义 __tablename__ 类属性,来指定这个模型映射到数 ...
- mini2440移植uboot 2014.04(三)
我修改的代码已经上传到github上,地址:https://github.com/qiaoyuguo/u-boot-2014.04-mini2440.git 参考文档: s3c2440手册(下载地址) ...
- hiho一下 第四十八周 拓扑排序·二【拓扑排序的应用 + 静态数组 + 拓扑排序算法的时间优化】
题目1 : 拓扑排序·二 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho所在学校的校园网被黑客入侵并投放了病毒.这事在校内BBS上立刻引起了大家的讨论,当 ...
- GVM管理Go版本
1.为什么要安装GVM 1.1什么是GVM GVM是一个golang虚拟环境配置工具,其允许一台机器上安装多个golang版本,gvm是第三方开发的Go多版本管理工具,类似ruby里面的rvm工具.使 ...
- Java钉钉开发_异常_01_error code:50002, error message:请求的员工userid不在授权范围内
一.异常原因: 请求的员工不在 应用的可见范围内. 如下图,应用的可见范围只设置为了部分员工.所以导致此异常 二.异常解决 将员工添加进可见范围即可
- 重写ScrollView实现两个ScrollView的同步滚动显示
1.背景介绍 最近项目用到两个ScrollView的同步显示,即拖动左边的ScrollView滚动的同时,实现右边的ScrollView同步滚动.此种情形常用在复杂界面布局中,比如左边的ScrollV ...
- 在Windows下编译WebRTC
前言 这篇文章的目的在于为你节省生命中宝贵的10小时(甚至更多),或者浪费你10分钟.作为Google更新频繁的大型跨平台基础库,WebRTC的编译一直被人称为噩梦.如果恰巧你偏要在Windows下编 ...
- Arc082_F Sandglass
Description有一个沙漏由两个上下相通玻璃球$A$和$B$构成,这两个玻璃球都含有一定量的沙子,我们暂且假定$A,B$中位于上方的玻璃球的为$U$,下方的玻璃球为$L$,则除非$U$中没有沙子 ...
- 【Google】循环字符串里面的独立子串
转载自九章算法(地址) 题目: 假设s是一个无限循环的字符串”abcdefghijklmnopqrstuvwxyz”,s就是一个”...zabcdefghijklmnopqrstuvwxyza...” ...