在教程1,教程2吮龄,教程3的帶領(lǐng)下,大家肯定迫不及待進(jìn)行教程4的開發(fā)了吧咆疗,這一章節(jié)漓帚,我們要完成現(xiàn)在這個(gè)webserver的所有功能,支持php動(dòng)態(tài)頁(yè)面以及報(bào)錯(cuò)404頁(yè)面的開發(fā)午磁,先展示下最終實(shí)現(xiàn)的界面
話不多說尝抖,先支持PHP再說
1.瀏覽器輸入
可見,他是指向當(dāng)前目錄下的index.php文件迅皇,所以先在這個(gè)目錄下創(chuàng)建文件
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>鵬哥的第一個(gè)web服務(wù)器</title>
</head>
<body>
<?php
$array = array(
"id" => "1",
"name"=> "pengge",
"aaa" => "sdsdd",
"yes" => "sdsdfsfsff"
);
echo "<pre>";
var_dump($array);
?>
</body>
</html>
2.順著教程3的思路昧辽,解析filename的文件名,發(fā)現(xiàn)他是.php文件登颓,所以我們不能直接文件讀取他的內(nèi)容搅荞,我們需要通過php腳本執(zhí)行該文件內(nèi)容,然后輸出到瀏覽器上
重寫wrap_response方法,使其支持php
/**
* @desc 封裝response 支持靜態(tài)頁(yè)面以及php頁(yè)面
*
*/
void wrap_response(int connfd,char *filename) {
struct stat sbuf;
int filefd,phpfd;
char *php_result;
char *srcp;
char response[MAXLINE],filetype[MAXLINE];
if(stat(filename,&sbuf) < 0) {
error_response(connfd);
exit(1);
}else {
//獲取文件類型
get_filetype(filename,filetype);
//打開文件并將其寫入內(nèi)存咕痛,并由瀏覽器展示
filefd = open(filename,O_RDONLY);
//走php腳本執(zhí)行輸出
if(strstr(filename, ".php")) {
sprintf(response, "HTTP/1.1 200 OK\r\n");
sprintf(response, "%sServer: Pengge Web Server\r\n",response);
sprintf(response, "%sConnection: close\r\n",response);
sprintf(response, "%sContent-type: %s\r\n\r\n",response,filetype);
Write(connfd, response, strlen(response));
printf("Response headers:\n");
printf("%s\n",response);
php_cgi(filename, connfd);
Close(connfd);
exit(1);
//走靜態(tài)頁(yè)面輸出
}else {
//拼接靜態(tài)文件的response頭
sprintf(response, "HTTP/1.0 200 OK\r\n");
sprintf(response, "%sServer: Pengge Web Server\r\n",response);
sprintf(response, "%sConnection: close\r\n",response);
sprintf(response, "%sContent-length: %lld\r\n",response,sbuf.st_size);
sprintf(response, "%sContent-type: %s\r\n\r\n",response,filetype);
Write(connfd, response, strlen(response));
printf("Response headers:\n");
printf("%s\n",response);
srcp = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
Close(filefd);
//清空srcp空間
Write(connfd, srcp, sbuf.st_size);
munmap(srcp, sbuf.st_size);
}
}
}
這里有個(gè)
php_cgi(filename, connfd);
這個(gè)方法痢甘,這個(gè)就是執(zhí)行php腳本輸出到頁(yè)面的方法,我們可以這樣寫
void php_cgi(char* script_path, int fd) {
dup2(fd, STDOUT_FILENO);
execl("/usr/bin/php","/usr/bin/php",script_path,(char *) NULL);
}
大家可以查查dup2跟execl的用法茉贡,這里不介紹了
綜上产阱,我們完成了支持PHP的webserver
接下來,我們要進(jìn)行404錯(cuò)誤界面的開發(fā)了块仆,
瀏覽器輸入
1.因?yàn)椴淮嬖谶@個(gè)頁(yè)面构蹬,所以我們需要像nginx一樣,創(chuàng)建一個(gè)404頁(yè)面
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>Pengge Server/1.0</center>
</body>
</html>
2.在stat(filename,&sbuf)方法中判斷
/**
* @desc 封裝response 支持靜態(tài)頁(yè)面以及php頁(yè)面
*
*/
void wrap_response(int connfd,char *filename) {
struct stat sbuf;
int filefd,phpfd;
char *php_result;
char *srcp;
char response[MAXLINE],filetype[MAXLINE];
if(stat(filename,&sbuf) < 0) {
error_response(connfd);
exit(1);
}else {
如果不存在悔据,我們就會(huì)讓他走一個(gè)error_sponse方法庄敛,這里就會(huì)包裝response頭信息以及文件
/**
* @desc 404頁(yè)面的response拼接
*
*/
void error_response(int connfd) {
struct stat sbuf;
int filefd;
char *srcp;
char error_body[MAXLINE],response[MAXLINE];
char filename[] = "./404.html";
stat(filename,&sbuf);
filefd = open(filename,O_RDONLY);
sprintf(response, "HTTP/1.1 404 Not found\r\n");
sprintf(response, "%sServer: Pengge Web Server\r\n",response);
sprintf(response, "%sConnection: close\r\n",response);
sprintf(response, "%sContent-length: %lld\r\n",response,sbuf.st_size);
sprintf(response, "%sContent-type: text/html\r\n\r\n",response);
Write(connfd, response, strlen(response));
printf("Response headers:\n");
srcp = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, filefd, 0);
Close(filefd);
//清空srcp空間
Write(connfd, srcp, sbuf.st_size);
munmap(srcp, sbuf.st_size);
}
這個(gè)時(shí)候重新打開瀏覽器,運(yùn)行
查看瀏覽器是不是有這樣的報(bào)錯(cuò)界面了
到此科汗,整個(gè)webserver到此結(jié)束了藻烤,撒花
最后,留給自己以及讀者討論的內(nèi)容有以下幾點(diǎn)
- 如何支持像nginx一樣的配置文件
- nginx中的epoll 模式能否支持
- 同樣是nginx中的功能头滔,如負(fù)載均衡怖亭,動(dòng)靜分離該怎么在配置文件中配置,并實(shí)現(xiàn)
- ...