|
C++Web编程
什么是CGI?
公共网关接口(CGI),是一套标准,定义了信息是如何在Web服务器和客户端脚本之间进行交换的。
CGI规范目前是由NCSA维护的,NCSA定义CGI如下:
公共网关接口(CGI),是一种用于外部网关程序与信息服务器(如HTTP服务器)对接的接口标准。
目前的版本是CGI/1.1,CGI/1.2版本正在推进中。
Web浏览
为了更好地了解CGI的概念,让我们点击一个超链接,浏览一个特定的网页或URL,看看会发生什么。
您的浏览器联系上HTTPWeb服务器,并请求URL,即文件名。
Web服务器将解析URL,并查找文件名。如果找到请求的文件,Web服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。
Web浏览器从Web服务器获取响应,并根据接收到的响应来显示文件或错误消息。
然而,以这种方式搭建起来的HTTP服务器,不管何时请求目录中的某个文件,HTTP服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。
公共网关接口(CGI),是使得应用程序(称为CGI程序或CGI脚本)能够与Web服务器以及客户端进行交互的标准协议。这些CGI程序可以用Python、PERL、Shell、C或C++等进行编写。
CGI架构图
下图演示了CGI的架构:
CGI架构
Web服务器配置
在您进行CGI编程之前,请确保您的Web服务器支持CGI,并已配置成可以处理CGI程序。所有由HTTP服务器执行的CGI程序,都必须在预配置的目录中。该目录称为CGI目录,按照惯例命名为/var/www/cgi-bin。虽然CGI文件是C++可执行文件,但是按照惯例它的扩展名是.cgi。
默认情况下,ApacheWeb服务器会配置在/var/www/cgi-bin中运行CGI程序。如果您想指定其他目录来运行CGI脚本,您可以在httpd.conf文件中修改以下部分:
AllowOverrideNone
OptionsExecCGI
Orderallow,deny
Allowfromall
OptionsAll
在这里,我们假设已经配置好Web服务器并能成功运行,你可以运行任意的CGI程序,比如Perl或Shell等。
第一个CGI程序
请看下面的C++程序:
#include
usingnamespacestd;
intmain()
{
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"HelloWorld-第一个CGI程序\n";
cout<<"\n";
cout<<"\n";
cout<<"HelloWorld!这是我的第一个CGI程序\n";
cout<<"\n";
cout<<"\n";
return0;
}
编译上面的代码,把可执行文件命名为cplusplus.cgi,并把这个文件保存在/var/www/cgi-bin目录中。在运行CGI程序之前,请使用chmod755cplusplus.cgiUNIX命令来修改文件模式,确保文件可执行。访问可执行文件,您会看到下面的输出:
HelloWorld!这是我的第一个CGI程序
上面的C++程序是一个简单的程序,把它的输出写在STDOUT文件上,即显示在屏幕上。在这里,值得注意一点,第一行输出Content-type:text/html\r\n\r\n。这一行发送回浏览器,并指定要显示在浏览器窗口上的内容类型。您必须理解CGI的基本概念,这样才能进一步使用Python编写更多复杂的CGI程序。C++CGI程序可以与任何其他外部的系统(如RDBMS)进行交互。
HTTP头信息
行Content-type:text/html\r\n\r\n是HTTP头信息的组成部分,它被发送到浏览器,以便更好地理解页面内容。HTTP头信息的形式如下:
HTTP字段名称:字段内容
例如
Content-type:text/html\r\n\r\n
还有一些其他的重要的HTTP头信息,这些在您的CGI编程中都会经常被用到。
头信息 描述
Content-type: MIME字符串,定义返回的文件格式。例如Content-type:text/html。
Expires:Date 信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为01Jan199812:00:00GMT。
Location:URL 这个URL是指应该返回的URL,而不是请求的URL。你可以使用它来重定向一个请求到任意的文件。
Last-modified:Date 资源的最后修改日期。
Content-length:N 要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。
Set-Cookie:String 通过string设置cookie。
CGI环境变量
所有的CGI程序都可以访问下列的环境变量。这些变量在编写CGI程序时扮演了非常重要的角色。
变量名 描述
CONTENT_TYPE 内容的数据类型。当客户端向服务器发送附加内容时使用。例如,文件上传等功能。
CONTENT_LENGTH 查询的信息长度。只对POST请求可用。
HTTP_COOKIE 以键&值对的形式返回设置的cookies。
HTTP_USER_AGENT 用户代理请求标头字段,递交用户发起请求的有关信息,包含了浏览器的名称、版本和其他平台性的附加信息。
PATH_INFO CGI脚本的路径。
QUERY_STRING 通过GET方法发送请求时的URL编码信息,包含URL中问号后面的参数。
REMOTE_ADDR 发出请求的远程主机的IP地址。这在日志记录和认证时是非常有用的。
REMOTE_HOST 发出请求的主机的完全限定名称。如果此信息不可用,则可以用REMOTE_ADDR来获取IP地址。
REQUEST_METHOD 用于发出请求的方法。最常见的方法是GET和POST。
SCRIPT_FILENAME CGI脚本的完整路径。
SCRIPT_NAME CGI脚本的名称。
SERVER_NAME 服务器的主机名或IP地址。
SERVER_SOFTWARE 服务器上运行的软件的名称和版本。
下面的CGI程序列出了所有的CGI变量。
#include
#include
usingnamespacestd;
conststringENV[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"};
intmain()
{
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"CGI环境变量\n";
cout<<"\n";
cout<<"\n";
cout<<"";
for(inti=0;i<24;i++)
{
cout<<""<";
//尝试检索环境变量的值
charvalue=getenv(ENV[i].c_str());
if(value!=0){
cout< }else{
cout<<"环境变量不存在。";
}
cout<<" | | \n";
}
cout<<" | <\n";
cout<<"\n";
cout<<"\n";
return0;
}
C++CGI库
在真实的实例中,您需要通过CGI程序执行许多操作。这里有一个专为C++程序而编写的CGI库,我们可以从ftp://ftp.gnu.org/gnu/cgicc/上下载这个CGI库,并按照下面的步骤安装库:
$tarxzfcgicc-X.X.X.tar.gz
$cdcgicc-X.X.X/
$./configure--prefix=/usr
$make
$makeinstall
您可以点击C++CGILibDocumentation,查看相关的库文档。
GET和POST方法
您可能有遇到过这样的情况,当您需要从浏览器传递一些信息到Web服务器,最后再传到CGI程序。通常浏览器会使用两种方法把这个信息传到Web服务器,分别是GET和POST方法。
使用GET方法传递信息
GET方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过?字符分隔开,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET方法是默认的从浏览器向Web服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串。当您向服务器传密码或其他一些敏感信息时,不要使用GET方法。GET方法有大小限制,在一个请求字符串中最多可以传1024个字符。
当使用GET方法时,是使用QUERY_STRINGhttp头来传递信息,在CGI程序中可使用QUERY_STRING环境变量来访问。
您可以通过在URL后跟上简单连接的键值对,也可以通过使用HTML
下面是上述表单的实际输出,请输入名和姓,然后点击提交按钮查看结果。
使用POST方法传递信息
一个更可靠的向CGI程序传递信息的方法是POST方法。这种方法打包信息的方式与GET方法相同,不同的是,它不是把信息以文本字符串形式放在URL中的?之后进行传递,而是把它以单独的消息形式进行传递。该消息是以标准输入的形式传给CGI脚本的。
我们同样使用cpp_get.cgi程序来处理POST方法。让我们以同样的例子,通过使用HTML表单和提交按钮来传递两个值,只不过这次我们使用的不是GET方法,而是POST方法,如下所示:
名:
姓:
向CGI程序传递复选框数据
当需要选择多个选项时,我们使用复选框。
下面的HTML代码实例是一个带有两个复选框的表单:
method="POST"
target="_blank">
数学
物理
下面的C++程序会生成cpp_checkbox.cgi脚本,用于处理Web浏览器通过复选框给出的输入。
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
CgiccformData;
boolmaths_flag,physics_flag;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"向CGI程序传递复选框数据\n";
cout<<"\n";
cout<<"\n";
maths_flag=formData.queryCheckbox("maths");
if(maths_flag){
cout<<"MathsFlag:ON"< }else{
cout<<"MathsFlag:OFF"< }
cout<<" \n";
physics_flag=formData.queryCheckbox("physics");
if(physics_flag){
cout<<"PhysicsFlag:ON"< }else{
cout<<"PhysicsFlag:OFF"< }
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
向CGI程序传递单选按钮数据
当只需要选择一个选项时,我们使用单选按钮。
下面的HTML代码实例是一个带有两个单选按钮的表单:
method="post"
target="_blank">
checked="checked"/>数学
物理
下面的C++程序会生成cpp_radiobutton.cgi脚本,用于处理Web浏览器通过单选按钮给出的输入。
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
CgiccformData;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"向CGI程序传递单选按钮数据\n";
cout<<"\n";
cout<<"\n";
form_iteratorfi=formData.getElement("subject");
if(!fi->isEmpty()&&fi!=(formData).end()){
cout<<"Radioboxselected:"< }
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
向CGI程序传递文本区域数据
当需要向CGI程序传递多行文本时,我们使用TEXTAREA元素。
下面的HTML代码实例是一个带有TEXTAREA框的表单:
method="post"
target="_blank">
请在这里输入文本...
下面的C++程序会生成cpp_textarea.cgi脚本,用于处理Web浏览器通过文本区域给出的输入。
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
CgiccformData;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"向CGI程序传递文本区域数据\n";
cout<<"\n";
cout<<"\n";
form_iteratorfi=formData.getElement("textcontent");
if(!fi->isEmpty()&&fi!=(formData).end()){
cout<<"TextContent:"< }else{
cout<<"Notextentered"< }
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
向CGI程序传递下拉框数据
当有多个选项可用,但只能选择一个或两个选项时,我们使用下拉框。
下面的HTML代码实例是一个带有下拉框的表单:
method="post"target="_blank">
数学
物理
下面的C++程序会生成cpp_dropdown.cgi脚本,用于处理Web浏览器通过下拉框给出的输入。
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
CgiccformData;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"向CGI程序传递下拉框数据\n";
cout<<"\n";
cout<<"\n";
form_iteratorfi=formData.getElement("dropdown");
if(!fi->isEmpty()&&fi!=(formData).end()){
cout<<"ValueSelected:"< }
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
在CGI中使用Cookies
HTTP协议是一种无状态的协议。但对于一个商业网站,它需要在不同页面间保持会话信息。例如,一个用户在完成多个页面的步骤之后结束注册。但是,如何在所有网页中保持用户的会话信息。
在许多情况下,使用cookies是记忆和跟踪有关用户喜好、购买、佣金以及其他为追求更好的游客体验或网站统计所需信息的最有效的方法。
它是如何工作的
服务器以cookie的形式向访客的浏览器发送一些数据。如果浏览器接受了cookie,则cookie会以纯文本记录的形式存储在访客的硬盘上。现在,当访客访问网站上的另一个页面时,会检索cookie。一旦找到cookie,服务器就知道存储了什么。
cookie是一种纯文本的数据记录,带有5个可变长度的字段:
Expires:cookie的过期日期。如果此字段留空,cookie会在访客退出浏览器时过期。
Domain:网站的域名。
Path:设置cookie的目录或网页的路径。如果您想从任意的目录或网页检索cookie,此字段可以留空。
Secure:如果此字段包含单词"secure",那么cookie只能通过安全服务器进行检索。如果此字段留空,则不存在该限制。
Name=Value:cookie以键值对的形式被设置和获取。
设置Cookies
向浏览器发送cookies是非常简单的。这些cookies会在Content-type字段之前,与HTTP头一起被发送。假设您想设置UserID和Password为cookies,设置cookies的步骤如下所示:
#include
usingnamespacestd;
intmain()
{
cout<<"Set-Cookie:UserID=XYZ;\r\n";
cout<<"Set-Cookie:Password=XYZ123;\r\n";
cout<<"Set-Cookie:Domain=www.wang027.com.cc;\r\n";
cout<<"Set-Cookie:Path=/perl;\n";
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"CGI中的Cookies\n";
cout<<"\n";
cout<<"\n";
cout<<"设置cookies"<
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
从这个实例中,我们了解了如何设置cookies。我们使用Set-CookieHTTP头来设置cookies。
在这里,有一些设置cookies的属性是可选的,比如Expires、Domain和Path。值得注意的是,cookies是在发送行"Content-type:text/html\r\n\r\n之前被设置的。
编译上面的程序,生成setcookies.cgi,并尝试使用下面的链接设置cookies。它会在您的计算机上设置四个cookies:
/cgi-bin/setcookies.cgi
获取Cookies
检索所有设置的cookies是非常简单的。cookies被存储在CGI环境变量HTTP_COOKIE中,且它们的形式如下:
key1=value1;key2=value2;key3=value3....
下面的实例演示了如何获取cookies。
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
Cgicccgi;
const_cookie_iteratorcci;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"CGI中的Cookies\n";
cout<<"\n";
cout<<"\n";
cout<<"";
//获取环境变量
constCgiEnvironment&env=cgi.getEnvironment();
for(cci=env.getCookieList().begin();
cci!=env.getCookieList().end();
++cci)
{
cout<<""<getName()<<" | ";
cout<getValue();
cout<<" | \n";
}
cout<<" | <\n";
cout<<" \n";
cout<<"\n";
cout<<"\n";
return0;
}
现在,编译上面的程序,生成getcookies.cgi,并尝试使用下面的链接获取您的计算机上所有可用的cookies:
/cgi-bin/getcookies.cgi
这会产生一个列表,显示了上一节中设置的四个cookies以及您的计算机上所有其他的cookies:
UserIDXYZ
PasswordXYZ123
Domainwww.w3cschool.cc
Path/perl
文件上传实例
为了上传一个文件,HTML表单必须把enctype属性设置为multipart/form-data。带有文件类型的input标签会创建一个"Browse"按钮。
action="/cgi-bin/cpp_uploadfile.cgi"
method="post">
文件:
这段代码的结果是下面的表单:
文件:选择文件
上传
注意:上面的实例已经故意禁用了保存上传的文件在我们的服务器上。您可以在自己的服务器上尝试上面的代码。
下面是用于处理文件上传的脚本cpp_uploadfile.cpp:
#include
#include
#include
#include
#include
#include
#include
#include
#include
usingnamespacestd;
usingnamespacecgicc;
intmain()
{
Cgicccgi;
cout<<"Content-type:text/html\r\n\r\n";
cout<<"\n";
cout<<"\n";
cout<<"CGI中的文件上传\n";
cout<<"\n";
cout<<"\n";
//获取要被上传的文件列表
const_file_iteratorfile=cgi.getFile("userfile");
if(file!=cgi.getFiles().end()){
//在cout中发送数据类型
cout<getDataType());
//在cout中写入内容
file->writeToStream(cout);
}
cout<<"<文件上传成功>\n";
cout<<"\n";
cout<<"\n";
return0;
}
上面的实例是在cout流中写入内容,但您可以打开文件流,并把上传的文件内容保存在目标位置的某个文件中。
|