借助xinetd可以实现一个简单的Web服务器,框架如下图所示
1.安装xinetd
sudo yum install xinetd
2.配置
举例:xinetd接收到客户端请求后,启动xhttpd的可执行文件,(xhttpd服务器的代码需要自己编写,用于处理/回应请求)
(1)在/etc/xinetd.d/下创建一个名为 xhttpd 的配置文件
注意:等号两边有1个空格,要想补齐必须使用Tab键
service xhttpd
{
socket_type=stream
protocol=tcp
wait=no
user=nobody
server=/usr/local/sbin/httpd/xhttpd # xhttpd是可执行文件,放在/usr/local/sbin/httpd目录下
/server_args=/var/xhttp/mh_html # 浏览器请求的资源所在的目录
disable=no
flags=IPv4
}
(2)vi /etc/services 添加端口
xhttpd 9523/tcp # xhttpd server
xhttpd 9523/udp
(3)重启xinetd服务器sudo service xinetd restart
(4)查看xinetd是否启动成功:ps aux | grep xinetd
root 15607 0.2 0.0 27164 1032 ? Ss 10:24 0:00 /usr/sbin/xinetd -stayalive -pidfile /var/run/xinetd.pid
gjw 15619 0.0 0.0 112724 988 pts/0 S+ 10:24 0:00 grep --color=auto xinetd
先看xhttpd的主函数
首先xinetd进程拉起xhttpd 传入参数argv[1]表示资源文件所在的目录
首先需要将工作目录切换到资源目录。
读取http 请求行
拆分http 请求行
然后判断是否符合要求
这里用stat
stat函数讲解
表头文件: #include <sys/stat.h>
#include <unistd.h>
定义函数: int stat(const char *file_name, struct stat *buf);
函数说明: 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
返回值: 执行成功则返回0,失败返回-1,错误代码存于errno
判断文件是否是目录,作目录的处理
如果是文件,作文件的处理
需要注意,这里xinetd通过输出重定向了,所以在xhttpd中只需要输出到标准输出即可
//xinetd ---> xhttpd argv[1]:浏览器请求的资源所在的目录/server_args=/var/xhttp/mh_html
int main(int argc,const char* argv[])
{
if(argc < 2)
send_error(500,"server error : argc < 2");
if(chdir(argv[1]) < 0)
send_error(500,"server error : chdir error");
char line[LEN],type[LEN],path[LEN],protocol[LEN];
//读取浏览器发来的请求,即:GET /lufei.jpg HTTP/1.1
if(fgets(line,sizeof(line),stdin) == NULL) //标准输入 ---> line
send_error(500,"server error : type path protocol");
if(sscanf(line,"%[^ ] %[^ ] %[^ ]",type,path,protocol) != 3) //line ---> type,path,protocol
send_error(400,"bad request");
if(strcasecmp(type,"GET") != 0) //type = GET
send_error(400,"method not allow");
if(path[0] != '/') // path = /lufei.jpg
send_error(404,"file not found");
while(fgets(line,sizeof(line),stdin) != NULL)
{
if(strcmp(line,"\n") == 0 || strcmp(line,"\r\n") == 0)
break;
}
char file[LEN];
struct stat st;
file[0] = '.';
decode(file+1,path);
//printf("%s\n",&path[1]);
//printf("%s\n",file);
if(stat(file,&st) < 0)
{
printf("file : %s\r\n",file);
send_error(500,"server error : stat");
}
if(S_ISDIR(st.st_mode))
{
send_header(200,"OK","text/html;charset=utf-8");
printf("<html>"
"<head><title>Index of %s</title></head>"
"<body bgcolor=\"#cc99cc\">"
"<h4>Index of %s</h4>"
"<pre>"
,file,file);
struct dirent** dl;
int nf = scandir(file,&dl,NULL,alphasort);
if(nf < 0)
perror("scandir");
else
{
struct stat fst;
char stfile[LEN];
for(int i=0;i<nf; ++i)
{
strcpy(stfile,file);
strcat(stfile,"/");
strcat(stfile,dl[i]->d_name);
if(lstat(stfile,&fst) < 0)
printf("<a href=\"%s%s/\">%-32.32s/</a>",file+1,dl[i]->d_name,dl[i]->d_name);
else if(S_ISDIR(fst.st_mode))
printf("<a href=\"%s%s/\">%-32.32s/</a> \t\t%14lld",file+1,dl[i]->d_name,dl[i]->d_name,(long long)fst.st_size);
else
printf("<a href=\"%s%s\">%-32.32s</a> \t\t%14lld",file+1,dl[i]->d_name,dl[i]->d_name,(long long)fst.st_size);
printf("<br/>");
}
}
printf("</pre>"
"</body>"
"</html>");
}
else
{
//普通文件
FILE* fp = fopen(file,"r");
if(fp == NULL)
send_error(500,"server error : open file");
send_header(200,"send header",get_filetype(file));
int ch;//这里必须用int判断EOF,我真是菜鸡。
while((ch = getc(fp)) != EOF)
{
putchar(ch);
}
fflush(stdout);
fclose(fp);
fp = NULL;
}
// printf("test success !\n");
return 0;
}
这里有一些编码问题,比较复杂,暂时不用管。
来看看发送响应头函数
void send_header(int status, char* title, char* filetype)
{
if(title == NULL || filetype == NULL)
{
title = "ERROR";
filetype = "text/plain; charset=utf-8";
}
printf("HTTP/1.1 %d %s\r\n",status,title);
printf("Content-Type:%s\r\n",filetype);
printf("\r\n");
}
void send_error(int status,char* title)
{
if(title == NULL)
title = "ERROR";
send_header(status,title,"text/html; charset=utf-8");
//将html的内容直接printf,就是回复给浏览器
printf("<html>\n"
"<head><title>%d %s</title></head>\n"
"<body bgcolor=\"#cc99cc\">\n"
"<h4>error!</h4>\n"
"<hr>\n"
"<address>\n"
"<a href=\"http://blog.csdn.net/gongluck93/\">gongluck</a>\n"
"</address>\n"
"</body>\n"
"</html>",
status,title);
fflush(stdout); //刷新标准输出缓冲区,让浏览器显示页面
exit(1);
}
判断文件类型
char* get_filetype(const char* file)
{
if(file == NULL)
return NULL;
char* dot = strrchr(file,'.');
if(*dot == '\0')
return "text/plain; charset=utf-8";
else if(strcmp(dot,".html") == 0)
return "text/html; charset=utf-8";
else if(strcmp(dot, ".jpg") == 0)
return "image/jpeg";
else if(strcmp(dot, ".gif") == 0)
return "image/gif";
else if(strcmp(dot, ".png") == 0)
return "image/png";
else if(strcmp(dot, ".wav") == 0)
return "audio/wav";
else if(strcmp(dot, ".avi") == 0)
return "video/x-msvideo";
else if(strcmp(dot, ".mov") == 0)
return "video/quicktime";
else if(strcmp(dot, ".mp3") == 0)
return "audio/mpeg";
else
return "text/plain; charset=utf-8";
}