用C一步步开发web服务器(4)

作者: jamespengge | 来源:发表于2017-03-04 13:48 被阅读289次

教程1教程2教程3的带领下,大家肯定迫不及待进行教程4的开发了吧,这一章节,我们要完成现在这个webserver的所有功能,支持php动态页面以及报错404页面的开发,先展示下最终实现的界面


话不多说,先支持PHP再说
1.浏览器输入

http://localhost:8000/index.php

可见,他是指向当前目录下的index.php文件,所以先在这个目录下创建文件

<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>鹏哥的第一个web服务器</title>
</head>
<body>
<?php
$array = array(
    "id" => "1",
    "name"=> "pengge",
    "aaa" => "sdsdd",
    "yes" => "sdsdfsfsff"
);
echo "<pre>";
var_dump($array);
?>
</body>
</html>

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页面

<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  支持静态页面以及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上了。多谢欣赏,希望能对大家有帮助原文章链接

相关文章

网友评论

  • 指尖流年:请教一下,如果后面带参数怎么解决,怎么把参数传进PHP运行文件?谢谢
    jamespengge:@指尖流年 我重新写了一篇文章,我找了下php源码,上面有一些这块的内容,你看看哈
    指尖流年: @指尖流年 传递参数倒是不难,可以通过$argv获得,但是实际开发中,通过$_POST或者$_GET获得的。问题是怎样通过$_POST或者$_GET内置产量获得?
    jamespengge:@指尖流年 这个我研究研究看看怎么处理,我尽快给你回复

本文标题:用C一步步开发web服务器(4)

本文链接:https://www.haomeiwen.com/subject/intjgttx.html