C++ Web编程

c++ web编程

 

1. 什么是 cgi?

  • 公共网关接口(cgi),是一套标准,定义了信息是如何在 web 服务器和客户端脚本之间进行交换的。
  • cgi 规范目前是由 ncsa 维护的,ncsa 定义 cgi 如下:
  • 公共网关接口(cgi),是一种用于外部网关程序与信息服务器(如 http 服务器)对接的接口标准。
  • 目前的版本是 cgi/1.1,cgi/1.2 版本正在推进中。

 

2. web 浏览

为了更好地了解 cgi 的概念,让我们点击一个超链接,浏览一个特定的网页或 url,看看会发生什么。

  • 您的浏览器联系上 http web 服务器,并请求 url,即文件名。
  • web 服务器将解析 url,并查找文件名。如果找到请求的文件,web 服务器会把文件发送回浏览器,否则发送一条错误消息,表明您请求了一个错误的文件。
  • web 浏览器从 web 服务器获取响应,并根据接收到的响应来显示文件或错误消息。

然而,以这种方式搭建起来的 http 服务器,不管何时请求目录中的某个文件,http 服务器发送回来的不是该文件,而是以程序形式执行,并把执行产生的输出发送回浏览器显示出来。

公共网关接口(cgi),是使得应用程序(称为 cgi 程序或 cgi 脚本)能够与 web 服务器以及客户端进行交互的标准协议。这些 cgi 程序可以用 python、perl、shell、c 或 c++ 等进行编写。

 

3. cgi 架构图

下图演示了 cgi 的架构:

cgi 架构

 

4. web 服务器配置

在您进行 cgi 编程之前,请确保您的 web 服务器支持 cgi,并已配置成可以处理 cgi 程序。所有由 http 服务器执行的 cgi 程序,都必须在预配置的目录中。该目录称为 cgi 目录,按照惯例命名为 /var/www/cgi-bin。虽然 cgi 文件是 c++ 可执行文件,但是按照惯例它的扩展名是 .cgi。

默认情况下,apache web 服务器会配置在 /var/www/cgi-bin 中运行 cgi 程序。如果您想指定其他目录来运行 cgi 脚本,您可以在 httpd.conf 文件中修改以下部分:

<directory "/var/www/cgi-bin">
   allowoverride none
   options execcgi
   order allow,deny
   allow from all
</directory>
 
<directory "/var/www/cgi-bin">
options all
</directory>

在这里,我们假设已经配置好 web 服务器并能成功运行,你可以运行任意的 cgi 程序,比如 perl 或 shell 等。

 

5. 第一个 cgi 程序

请看下面的 c++ 程序:

#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 - 第一个 cgi 程序</title>\n";
   cout << "</head>\n";
   cout << "<body>\n";
   cout << "<h2>hello world! 这是我的第一个 cgi 程序</h2>\n";
   cout << "</body>\n";
   cout << "</html>\n";
   
   return 0;
}

编译上面的代码,把可执行文件命名为 cplusplus.cgi,并把这个文件保存在 /var/www/cgi-bin 目录中。在运行 cgi 程序之前,请使用 chmod 755 cplusplus.cgi unix 命令来修改文件模式,确保文件可执行。访问可执行文件,您会看到下面的输出:

 

6. hello world! 这是我的第一个 cgi 程序

上面的 c++ 程序是一个简单的程序,把它的输出写在 stdout 文件上,即显示在屏幕上。在这里,值得注意一点,第一行输出 content-type:text/html\r\n\r\n。这一行发送回浏览器,并指定要显示在浏览器窗口上的内容类型。您必须理解 cgi 的基本概念,这样才能进一步使用 python 编写更多复杂的 cgi 程序。c++ cgi 程序可以与任何其他外部的系统(如 rdbms)进行交互。

 

7. 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 信息变成无效的日期。浏览器使用它来判断一个页面何时需要刷新。一个有效的日期字符串的格式应为 01 jan 1998 12:00:00 gmt。
location: url 这个 url 是指应该返回的 url,而不是请求的 url。你可以使用它来重定向一个请求到任意的文件。
last-modified: date 资源的最后修改日期。
content-length: n 要返回的数据的长度,以字节为单位。浏览器使用这个值来表示一个文件的预计下载时间。
set-cookie: string 通过 string 设置 cookie。

 

8. 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 <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 环境变量</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 << "环境变量不存在。";
       }
       cout << "</td></tr>\n";
   }
   cout << "</table><\n";
   cout << "</body>\n";
   cout << "</html>\n";
   
   return 0;
}

 

9. c++ cgi 库

在真实的实例中,您需要通过 cgi 程序执行许多操作。这里有一个专为 c++ 程序而编写的 cgi 库,我们可以从 ftp://ftp.gnu.org/gnu/cgicc/ 上下载这个 cgi 库,并按照下面的步骤安装库:

$tar xzf cgicc-x.x.x.tar.gz 
$cd cgicc-x.x.x/ 
$./configure --prefix=/usr 
$make
$make install

您可以点击 c++ cgi lib documentation,查看相关的库文档。

 

10. get 和 post 方法

您可能有遇到过这样的情况,当您需要从浏览器传递一些信息到 web 服务器,最后再传到 cgi 程序。通常浏览器会使用两种方法把这个信息传到 web 服务器,分别是 get 和 post 方法。

 

11. 使用 get 方法传递信息

get 方法发送已编码的用户信息追加到页面请求中。页面和已编码信息通过 ? 字符分隔开,如下所示:

http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2

get 方法是默认的从浏览器向 web 服务器传信息的方法,它会在浏览器的地址栏中生成一串很长的字符串。当您向服务器传密码或其他一些敏感信息时,不要使用 get 方法。get 方法有大小限制,在一个请求字符串中最多可以传 1024 个字符。

当使用 get 方法时,是使用 query_string http 头来传递信息,在 cgi 程序中可使用 query_string 环境变量来访问。

您可以通过在 url 后跟上简单连接的键值对,也可以通过使用 html <form> 标签的 get 方法来传信息。

 

12. 简单的 url 实例:get 方法

下面是一个简单的 url,使用 get 方法传递两个值给 hello_get.py 程序。

/cgi-bin/cpp_get.cgi?first_name=zara&last_name=ali

下面的实例生成 cpp_get.cgi cgi 程序,用于处理 web 浏览器给出的输入。通过使用 c++ cgi 库,可以很容易地访问传递的信息:

#include <iostream>
#include <vector>  
#include <string>  
#include <stdio.h>  
#include <stdlib.h> 

#include <cgicc/cgidefs.h> 
#include <cgicc/cgicc.h> 
#include <cgicc/httphtmlheader.h> 
#include <cgicc/htmlclasses.h>  

using namespace std;
using namespace cgicc;

int main ()
{
   cgicc formdata;
   
   cout << "content-type:text/html\r\n\r\n";
   cout << "<html>\n";
   cout << "<head>\n";
   cout << "<title>使用 get 和 post 方法</title>\n";
   cout << "</head>\n";
   cout << "<body>\n";

   form_iterator fi = formdata.getelement("first_name");  
   if( !fi->isempty() && fi != (*formdata).end()) {  
      cout << "名:" << **fi << endl;  
   }else{
      cout << "no text entered for first name" << endl;  
   }
   cout << "<br/>\n";
   fi = formdata.getelement("last_name");  
   if( !fi->isempty() &&fi != (*formdata).end()) {  
      cout << "姓:" << **fi << endl;  
   }else{
      cout << "no text entered for last name" << endl;  
   }
   cout << "<br/>\n";

   cout << "</body>\n";
   cout << "</html>\n";
   
   return 0;
}                
相关文章