在教程1,教程2,教程3的帶領(lǐng)下,大家肯定迫不及待進(jìn)行教程4的開發(fā)了吧,這一章節(jié),我們要完成現(xiàn)在這個webserver的所有功能,支持php動態(tài)頁面以及報錯404頁面的開發(fā),先展示下最終實現(xiàn)的界面


話不多說,先支持PHP再說
1.瀏覽器輸入
可見,他是指向當(dāng)前目錄下的index.php文件,所以先在這個目錄下創(chuàng)建文件
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>鵬哥的第一個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)頁面以及php頁面
*
*/
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)頁面輸出
}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);
}
}
}
這里有個
php_cgi(filename, connfd);
這個方法,這個就是執(zhí)行php腳本輸出到頁面的方法,我們可以這樣寫
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錯誤界面的開發(fā)了,
瀏覽器輸入
1.因為不存在這個頁面,所以我們需要像nginx一樣,創(chuàng)建一個404頁面
<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)頁面以及php頁面
*
*/
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 {
如果不存在,我們就會讓他走一個error_sponse方法,這里就會包裝response頭信息以及文件
/**
* @desc 404頁面的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);
}
這個時候重新打開瀏覽器,運行
查看瀏覽器是不是有這樣的報錯界面了

到此,整個webserver到此結(jié)束了,撒花
最后,留給自己以及讀者討論的內(nèi)容有以下幾點
- 如何支持像nginx一樣的配置文件
- nginx中的epoll 模式能否支持
- 同樣是nginx中的功能,如負(fù)載均衡,動靜分離該怎么在配置文件中配置,并實現(xiàn)
- ...