0


[项目][WebServer][CGI机制 && 设计]详细讲解

目录


1.何为CGI机制?

  • CGI(Common Gateway Interface)是外部应用程序(CGI程序)与WEB服务器之间的接口标准,是在CGI程序和WEB服务器之间传递信息的过程请添加图片描述

2.理解CGI机制

  • 真正理解CGI并不简单,先从现象入手 - 浏览器除了从服务器获得资源(网页、图片、文字等),有时候还能上传一些东西(提交表单、注册用户之类)- 目前HttpServer只能进行获得资源,并不能进行资源上传,所以目前HttpServer并不具有交互式功能- 为了让网站能够实现交互式,需要使用CGI功能完成
  • 理论上,可以使用任何语言来编写CGI程序 - 注意:http提供CGI机制,和CGI程序是两码事 - 如:学校(http)提供教学平台(CGI机制),学生(CGI程序)来学习
  • 实现上理解,首先要理解GET方法和POST方法的区别 - GET方法从浏览器传参给http服务器时,是将参数跟套URI后面的- 如:www.baidu.com/test_cgi?x=100&y=200- GET方法,如果没有传参,http按照一般的方式进行,返回资源即可 - 静态网页、各种资源- GET方法,如果有参数传入,http就需要按照CGI方式处理参数,并将执行结果(期望资源)返回给浏览器- POST方法从浏览器传参给http服务器时,是将参数放到请求正文的- POST方法,一般都需要使用CGI方式来进行处理
  • 什么时候,需要使用CGI来进行数据处理呢?- 只要用户有数据上传上来
  • 如何看待CGI程序呢? - 子CGI程序的标准输入时浏览器- 子CGI程序的标准输出时浏览器- 通信细节由http完成
  • 浏览器和Server进行数据交互的本质,就是进程间通信,也是socket通信的本质请添加图片描述

3.CGI接口设计

1.ProcessNonCgi

  • 该接口处理静态网页的请求,只需要将静态网页打开即可
intProcessNonCgi(){
    _response.fd =open(_request.path.c_str(), O_RDONLY);if(_response.fd >=0){return OK;}return NOT_FOUND;}

2.ProcessCgi

  • 父子进程间如何传递数据?- POST参数、子进程处理结果用匿名管道通信- 请求方法、GET参数用环境变量通信 - 环境变量是具有全局属性的,可以被子进程继承下去,不受exec*程序替换的影响
  • 给子进程传递环境变量时,亲测用setenv()putenv()靠谱些 - putenv()不会复制传递的字符串大小,而setenv()会的
  • 注意:程序替换,只替换代码和数据,并不替换内核进程相关的数据结构 - 如文件描述符表- 在程序替换之后,数据没有了,但是曾经打开的文件PIPE还在
  • 进程替换之后,子进程如何得知,对应的读写文件描述符时多少呢?- 虽然替换后子进程不知道对应的读写fd,但是一定知道0 && 1- 此时不需要知道读写fd了,只需要读0写1即可- 在执行exec*前,dup2重定向
intProcessCgi(){int code =0;// 退出码
    std::string &bin = _request.path;// 父子间通信用匿名管道 // TODO 待整理int input[2];// 父进程读int output[2];// 父进程写if(pipe(input)<0){LOG(ERROR,"Pipe Input Error");
        code = SERVER_ERROR;return code;}if(pipe(output)<0){LOG(ERROR,"Pipe Output Error");
        code = SERVER_ERROR;return code;}

    pid_t id =fork();if(id ==0)// Child{close(output[1]);close(input[0]);// 子进程如何知道方法是什么?setenv("METHOD", _request.method.c_str(),1);// GET带参通过环境变量导入子进程if(_request.method =="GET"){setenv("ARG", _request.arg.c_str(),1);LOG(INFO,"GET Method, Add ARG");}elseif(_request.method =="POST"){setenv("CLENGTH", std::to_string(_request.content_length).c_str(),1);LOG(INFO,"POST Method, Add Content_Length");}else{// Do Nothing}// 进程替换之后,子进程如何得知,对应的读写文件描述符是多少呢?// 虽然替换后子进程不知道对应读写fd,但是一定知道0 && 1// 此时不需要知道读写fd了,只需要读0写1即可// 在exec*执行前,dup2重定向dup2(input[1],1);dup2(output[0],0);execl(bin.c_str(), bin.c_str(),nullptr);exit(5);}elseif(id <0){LOG(ERROR,"Fork Error");
        code = SERVER_ERROR;return code;}else// Parent{close(output[0]);close(input[1]);if(_request.method =="POST"){// 不能确保一次性就能写完,所以constchar*start = _request.request_body.c_str();int size =0, total =0;while(total < _request.request_body.size()&&(size =write(output[1], start + total, _request.request_body.size()- total)>0)){
                total += size;}}// 读取CGI子进程的处理结果char ch ='K';while(read(input[0],&ch,1)>0){// CGI执行完之后的结果,并不可以直接返回给浏览器,因为这部分内容只是响应正文
            _response.response_body += ch;}int status =0;
        pid_t ret =waitpid(id,&status,0);if(ret == id){if(WIFEXITED(status)){if(WEXITSTATUS(status)==0){
                    code = OK;}else{
                    code = BAD_REQUEST;}}else{
                code = SERVER_ERROR;}}close(output[1]);close(input[0]);}return OK;}

本文转载自: https://blog.csdn.net/qq_37281656/article/details/142254952
版权归原作者 DieSnowK 所有, 如有侵权,请联系我们删除。

“[项目][WebServer][CGI机制 && 设计]详细讲解”的评论:

还没有评论