CGI入门一:使用C++实现CGI程序

为什么需要CGI

最早的Web服务器简单地响应浏览器发来的HTTP请求,并将存储在服务器上的HTML文件返回给浏览器,也就是静态html。事物总是不 断发展,网站也越来越复杂,所以出现动态技术。但是服务器并不能直接运行 php,asp这样的文件,自己不能做,外包给别人吧,但是要与第三做个约定,我给你什么,然后你给我什么,就是我把请求参数发送给你,然后我接收你的处理结果给客户端。那这个约定就是 common gateway interface,简称cgi。这个协议可以用vb,c,php,python 来实现。
在这里插入图片描述

WEB服务器与CGI程序交互

WEB服务器将根据CGI程序的类型决定数据向CGI程序的传送方式,一般来讲是通过标准输入/输出流和环境变量来与CGI程序间传递数据。
在这里插入图片描述
CGI程序通过标准输入(STDIN)和标准输出(STDOUT)来进行输入输出。此外CGI程序还通过环境变量来得到输入,操作系统提供了许 多环境变量,它们定义了程序的执行环境,应用程序可以存取它们。Web服务器和CGI接口又另外设置了一些环境变量,用来向CGI程序传递一些重要的参 数。HTTP协议的GET方法通过环境变量QUERY-STRING向CGI程序传递Form中的数据。每个CGI程序只能处理一个用户请求,所以在激活一个CGI程序进程时也创建了属于该进程的环境变量。CGI程序在处理完一个请求后就退出,在下一个请求到来时再创建一个新进程执行CGI程序。

下面是一些常用的CGI环境变量:
在这里插入图片描述

FastCGI

CGI工作原理:每当客户请求CGI的时候,WEB服务器就请求操作系统生成一个新的CGI解释器进程(如php-cgi.exe),CGI 的一个进程则处理完一个请求后退出,下一个请求来时再创建新进程。当然,这样在访问量很少没有并发的情况也行。可是当访问量增大,并发存在,这种方式就不 适合了。于是就有了fastcgi。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。
一般情况下,FastCGI的整个工作流程是这样的:

  1. Web Server启动时载入FastCGI进程管理器(IIS ISAPI或Apache Module)
  2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程(可见多个php-cgi)并等待来自Web Server的连接。
  3. 当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。 Web server将CGI环境变量和标准输入发送到FastCGI子进程php-cgi。
  4. FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时, 请求便告处理完成。FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在Web Server中)的下一个连接。 在CGI模式中,php-cgi在此便退出了。

PHP-FPM与Spawn-FCGI
  Spawn-FCGI是一个通用的FastCGI管理服务器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI进行FastCGI模式下的管理工作。 但是有缺点,于是PHP-fpm就是针对于PHP的,Fastcgi的一种实现,他负责管理一个进程池,来处理来自Web服务器的请求。目前,PHP-fpm是内置于PHP的。

使用C++实现CGI

安装Apache服务器,将C++实现的CGI程序编译的可执行文件复制到Apache的cgi-bin目录下。

#include <iostream>
using namespace std;
int main (){
   cout << "Content-type:text/html\r\n\r\n";
   cout << "<html>\n";
   cout << "<head>\n";
   cout << "<title>Hello World - First CGI Program</title>\n";
   cout << "</head>\n";
   cout << "<body>\n";
   cout << "<h2>Hello World! This is my first CGI program</h2>\n";
   cout << "</body>\n";
   cout << "</html>\n";   
   return 0;
}

上述程序手动执行效果:
在这里插入图片描述
在浏览器中输入"http://42.96.142.129:8083/cgi-bin/test",浏览器输出内容:
在这里插入图片描述

获取CGI的环境变量

#include <iostream>
#include <stdlib.h>
using namespace std;
const string ENV[24] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", 
 "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING",
 "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", 
 "HTTP_HOST", "HTTP_USER_AGENT", "PATH", 
 "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",
 "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME",
 "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN",
 "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL",
 "SERVER_SIGNATURE","SERVER_SOFTWARE"};  
 int main(){
 	cout<<"Content-type:text/html\r\n\r\n";
 	cout<<"<html>\n";
 	cout<<"<head>\n";
 	cout<<"<title>CGI Envrionment Variables</title>\n";
 	cout<<"</head>\n";
 	cout<<"<body>\n";
 	cout<<"<table border=\"0\"cellspacing=\"2\">";
 	for(int i=0;i<24;i++){
  		cout<<"<tr><td>"<<ENV[i]<<"</td><td>";
  		char *value = getenv(ENV[i].c_str()); 
  		if(value!= 0){
      			cout << value;
  		}else{
      			cout<<"Environment variable does not exist.";
  		}
  		cout<<"</td></tr>\n";
 	}
 	cout<<"</table><\n";
 	cout<<"</body>\n";
 	cout<<"</html>\n"; 
        return 0;
}

在这里插入图片描述

GET参数的获取

在GET方法下,CGI程序无法直接从服务器的标准输入中获取数据,服务器把它从http协议接收到的数据编码到环境变量QUERY_STRING(或PATH_INFO)。采用GET方法时,只需把这些数据附加到URL的末尾,如http://42.96.142.129:8083/cgi-bin/1023?a=1&b=2&c=3,这时候QUERY_STRING的值为a=1&b=2&c=3。

#include<iostream>
#include<stdlib.h>
#include<vector>
#include<string>
using namespace std;
vector<string> StringSplit(const string& sData, const string& sDelim){
 	vector<string>vItems;
 	vItems.clear();
 	string::size_type bpos = 0;
 	string::size_type epos = 0;
 	string::size_type nlen = sDelim.size();
 	while ((epos=sData.find(sDelim, epos)) != string::npos){
		vItems.push_back(sData.substr(bpos, epos-bpos));
  		epos += nlen;
  		bpos = epos;
    	}
 vItems.push_back(sData.substr(bpos, sData.size()-bpos));
 return vItems;
}
int main(){
 cout << "Content-type:text/html\r\n\r\n";
 cout << "<html>\n";
 cout << "<head>\n";
 cout << "<title>Hello World - First CGI Program</title>\n";
 cout << "</head>\n";
 cout << "<body>\n"; 
 cout<<"get parameter:<br/>";
 char *value = getenv("QUERY_STRING");  
 if(value!= 0){
     /*"a=1&b=2&c=3"*/
    vector<string>paras=StringSplit((const string)value,"&");
    vector<string>::iterator iter=paras.begin();
    for(;iter!=paras.end();iter++){
           vector<string>singlepara=StringSplit(*iter,"=");
        cout<<singlepara[0]<<" "<<singlepara[1]<<"<br/>";
    }
    cout << "</body>\n";
    cout << "</html>\n";   
    return 0;
}

在这里插入图片描述

POST参数的获取

在POST方法下,CGI程序可以直接从服务器的标准输入中获取数据,不过要先从CONTENT_LENGTH这个环境变量中得到POST参数的长度,然后再读取相应长度的内容。
post.html代码

<html>
<head>
<title>CGI Post</title>
</head>
<body>
<tr><td>
<div style="font-weight:bold; font-size:15px">Method: POST</div>
<div>lease input two number:<div>
<form method="post" action="./cgi-bin/post">
<input type="txt" size="3" name="m">*
<input type="txt" size="3" name="n">=
<input type="submit" value="result">
</form>
</td></tr>
</table>
</body>
</html>

将post.html复制到htdocs目录下
在这里插入图片描述

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
int main(){
 cout << "Content-type:text/html\r\n\r\n";
 cout << "<html>\n";
 cout << "<head>\n";
 cout << "<title>Testing Post</title>\n";
 cout << "</head>\n";
 cout << "<body>\n"; 
 char *lenstr =getenv("CONTENT_LENGTH");
 if(lenstr==NULL){
  	cout<<"Error, CONTENT_LENGTH should be entered!"<<"<br/>";
 }else{
  	int len=atoi(lenstr);
  	char poststr[20];
  	fgets(poststr,len+1,stdin);
  	cout<<"poststr:"<<poststr<<"<br/>"; 
  	char m[10],n[10];
  	if(sscanf(poststr,"m=%[^&]&n=%s",m,n)!=2){
   		cout<<"Error: Parameters are not right!<br/>";
  	}else{
   		cout<<"m * n = "<<atoi(m)*atoi(n)<<"<br/>";
  	}
     }
     cout << "</body>\n";
     cout << "</html>\n";   
     return 0;
}	

在这里插入图片描述
在浏览器中输入"http://42.96.142.129:8083/post.html",在输入框中分别输入3和4,点击result按钮,输出如上图所示的结果。

使用PHP实现CGI的情况

记得曾在xp 配置 apache + php ,会在apache 配置下面一段:

LoadModule php5_module C:/php/php5apache2_2.dll

当PHP需要在Apache服务器下运行时,一般来说,它可以模块的形式集成, 此时模块的作用是接收Apache传递过来的PHP文件请求,并处理这些请求, 然后将处理后的结果返回给Apache。如果我们在Apache启动前在其配置文件中配置好了PHP模块, PHP模块通过注册apache2的ap_hook_post_config挂钩,在Apache启动的时候启动此模块以接受PHP文件的请求。
Apache 的Hook机制是指:Apache 允许模块(包括内部模块和外部模块,例如mod_php5.so,mod_perl.so等)将自定义的函数注入到请求处理循环中。 换句话说,模块可以在Apache的任何一个处理阶段中挂接(Hook)上自己的处理函数,从而参与Apache的请求处理过程。 mod_php5.so/ php5apache2.dll就是将所包含的自定义函数,通过Hook机制注入到Apache中,在Apache处理流程的各个阶段负责处理php请 求。

仿真使用STDOUT和STDIN与Web server交互

close(STDOUT_FILENO);
dup(connfd);  //connfd是sockert句柄
printf("abcd\n");
close(connfd);

先关闭标准输出文件描述符STDOUT_FILENO(其值是1)。使用dup复制socket文件描述符connfd。因为dup总返回系统中最小的可用文件描述符,所以返回值实际是1,即之前关闭的标准输出文件描述符的值。这样服务器输出到标准输出的内容就会直接发送到与客户连接对应的socket上。

参考:
https://www.cnblogs.com/wanghetao/p/3934350.html

  • 14
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值