在教程1,教程2,教程3的带领下,大家肯定迫不及待进行教程4的开发了吧,这一章节,我们要完成现在这个webserver的所有功能,支持php动态页面以及报错404页面的开发,先展示下最终实现的界面
话不多说,先支持PHP再说
1.浏览器输入
http://localhost:8000/index.php
可见,他是指向当前目录下的index.php文件,所以先在这个目录下创建文件
鹏哥的第一个web服务器
"1",
"name"=> "pengge",
"aaa" => "sdsdd",
"yes" => "sdsdfsfsff"
);
echo "";
var_dump($array);
?>
2.顺着教程3的思路,解析filename的文件名,发现他是.php文件,所以我们不能直接文件读取他的内容,我们需要通过php脚本执行该文件内容,然后输出到浏览器上
重写wrap_response方法,使其支持php
/**
* @desc 封装response 支持静态页面以及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);
//打开文件并将其写入内存,并由浏览器展示
filefd = open(filename,O_RDONLY);
//走php脚本执行输出
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);
//走静态页面输出
}else {
//拼接静态文件的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);
这个方法,这个就是执行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
接下来,我们要进行404错误界面的开发了,
浏览器输入
http://localhost:8000/index1.php
1.因为不存在这个页面,所以我们需要像nginx一样,创建一个404页面
404 Not Found
404 Not Found
Pengge Server/1.0
2.在stat(filename,&sbuf)方法中判断
/**
* @desc 封装response 支持静态页面以及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);
}
这个时候重新打开浏览器,运行
http://localhost:8000/index1.php
查看浏览器是不是有这样的报错界面了
到此,整个webserver到此结束了,撒花
最后,留给自己以及读者讨论的内容有以下几点
- 如何支持像nginx一样的配置文件
- nginx中的epoll 模式能否支持
- 同样是nginx中的功能,如负载均衡,动静分离该怎么在配置文件中配置,并实现
- ...
最后献上我的代码,我传到我的github上了。多谢欣赏,希望能对大家有帮助原文章链接