技术中心
 
 

CGI脚本工作原理

   日期:2009-07-30     来源:互联网    
CGI脚本工作原理

引言


新的网站设计人员在建立网站之后经常问到一个问题就是:“什么是CGI脚本,如何在我的网站上使用它?”或“如何在我的网站上创建交互式表单?”。

在本文中,我们将回答有关CGI脚本的问题,并演示如何创建自己的脚本。

在此过程中,您还将了解有关Web服务器的一些知识。让我们开始吧!

Web服务器

如Web服务器工作原理一文中所述,Web服务器相当简单。从根本上说,Web服务器只是从磁盘中检索出文件,并通过网络将其发送到发出请求的浏览器。假设您键入URL:http://www.bygpub.com/books/tg2rw/author.htm。Web服务器将会获得一个针对文件/books/tg2rw/author.htm的请求。查看下图,您可以了解服务器是如何解决该请求的:


在安装过程中,Web服务器已被告知c:My Documentswww是服务器的根目录。然后,该服务器会从根目录中找出/books/tg2rw/author.htm。当您请求URL http://www.bygpub.com/books/tg2rw/时,该服务器就会知道您正在查找该目录的默认文件。它会查找一些不同的文件名以试图找到默认文件:index.html、index.htm、default.html和default.htm。根据服务器的不同,它也可以查找其他文件名。因此,该服务器会将 http://www.bygpub.com/books/tg2rw/转换为 http://www.bygpub.com/books/tg2rw/index.htm并传送该文件。所有其他文件必须通过明确给出文件名来指定。

这就是所有Web服务器处理静态文件的方式。大多数Web服务器还可以通过称作通用网关接口(CGI)的机制处理动态文件。您已经在Web上的各种地方看到过CGI,只不过您当时可能对它并不了解。例如:

您可以在任何留言簿上通过HTML表单输入消息,当下一次查看此留言簿时,页面将会包含您输入的新条目。
Network Solutions上的WHOIS表单允许您在表单上输入域名,返回的网页将根据输入的域名而有所不同。
任何搜索引擎都允许您在HTML表单上输入关键字,然后它将根据您输入的关键字动态创建页面。

所有这些动态页面使用的都是CGI。

CGI机制

在大多数Web服务器上,通过以下方式使CGI机制标准化。在服务器视为根目录的普通目录树中,可以创建一个名为cgi-bin的子目录。(可以在上一页的图示中查看此目录。)然后,服务器了解到不应只是读取和发送从特殊cgi-bin目录中请求的任何文件,而应执行它们。执行的程序的输出内容是它实际发送给请求该页的浏览器的内容。一般来说,可执行程序既可以是一个纯可执行程序(就像C编译器的输出),也可以是一个PERL脚本。PERL是一种用于CGI脚本的最流行的语言。

假设您在浏览器中键入URL:http://www.howstuffworks.com/cgi-bin/search.pl。由于服务器了解到search.pl位于cgi-bin目录中,因此它会执行search.pl(一个PERL脚本),并将执行的输出内容发送到浏览器。

您可以编写自己的脚本并试验CGI,前提是:

您了解某种编程语言,如C或PERL。
您对处理CGI脚本的Web服务器拥有访问权限。如果您已为用于承载您的网站的Web托管服务付费,则可以选择通过您的主机来访问CGI脚本。有关详细信息,请查看托管服务。如果您没有为用于承载您的网站的Web主机服务付费,则可以通过在家用计算机上安装Web服务器并学习如何使用它来进行试验。尽管第二种选择要稍微复杂一些,但您在此过程中肯定可以学到很多知识!

简单的CGI脚本

假定您对cgi-bin目录拥有访问权限(请参阅上一节),了解C编程语言或PERL,则可以使用CGI执行一系列有趣的试验。让我们从创建最简单的CGI脚本开始。

在网页基础知识一文中,我们研究了可能存在的最简单的HTML网页。这个网页的形式如下:

这个可能存在的最简单的CGI脚本在执行时,将会创建上述的简单的静态页面作为其输出。以下是用C语言编写此CGI程序的形式:

在Web服务器上,将此程序输入到文件simplest.c中,然后通过以下命令进行编译:

(有关如何编译C语言程序的详细信息,请参见C语言入门教程。)

将simplest.cgi放置在cgi-bin目录中,它就可以执行了。您现在就可以通过键入或单击此URL来尝试一下:http://www.howstuffworks.com/cgi-bin/simplest.cgi。您可以看到,该脚本所做的全部工作就是生成显示“Hello there!”的页面。唯一没有预料到的部分就是下面这行内容:

“Content-type:text/htmlnn”这行内容是一段特殊的文本,任何CGI脚本必须首先将它发送给浏览器。只要您记住这一点,就一切ok了。如果您忘记了这一点,则浏览器将拒绝输出脚本。

您可以在PERL中执行相同的操作。将下面这段PERL代码键入到一个名为simplest.pl的文件中:

将该文件放置到cgi-bin目录中。在UNIX计算机上,键入以下内容也许会有用:

这将告知UNIX该脚本是可执行的。您现在就可以通过键入或单击此URL来尝试一下:http://www.howstuffworks.com/cgi-bin/simplest.pl。

您现在已经了解了CGI脚本的基本概念。相当简单!实际上就是,执行一个程序,然后将程序的输出发送到调用脚本的浏览器。发送给stdout的正常输出就是发送给浏览器的输出。

不过,CGI脚本的重点在于创建动态内容——每次执行脚本时,输出应该是不同。毕竟,如果每次运行脚本时的输出都是相同的,您也可以使用静态页面。以下C语言程序演示了非常简单的动态内容:

使用一个文本编辑器,将此程序键入到一个名为count.c的文件中。通过键入以下命令对其进行编译:

创建另一个名为count.txt的文本文件,并在其中放入一个零。将counter.cgi和count.txt放置在cgi-bin目录中,即可以运行脚本。您现在就可以通过键入或单击此URL来尝试一下:http://www.howstuffworks.com/cgi-bin/count.cgi。您可以看到,脚本所做的全部工作就是生成一个显示“The current count is:X”的页面。运行一次脚本,其中的X就会递增一次。尝试将脚本运行几次,并注意观察页面内容的变化!

count.txt文件保留当前计数,同时小的incrementcount()函数使count.txt文件中的计数递增。此函数将打开count.txt文件、从中读取数字、递增数字并将其写回到文件中。实际上,此函数会尝试打开文件两次。这样做是为了防止两个用户试图同时访问该文件。这项技术并非万无一失,但对于这类简单情况还是有效的。如果在第二次尝试时无法打开文件,则将为调用者返回错误值-1。更加完善的程序将识别返回值-1,并会生成相应的错误提示。

    gcc count.c -o count.cgi 
 
#include

int incrementcount()
{
FILE *f;
int i;

f=fopen("count.txt", "r+");
if (!f)
{
sleep(1);
f=fopen("count.txt", "r+");
if (!f)
return -1;
}

fscanf(f, "%d", andi);
i++;
fseek(f,0,SEEK_SET);
fprintf(f, "%d", i);
fclose(f);
return i;
}

int main()
{
printf("Content-type:text/htmlnn");
printf("n");
printf("n");
printf("

The current count is:")
printf("%dn", incrementcount());
printf("n");
printf("n");
return 0;
}

chmod 755 simplest.pl
#! /usr/bin/perl
print andquot;Content-type:text/htmlnnandquot;;
print andquot;andlt;htmlandgt;andlt;bodyandgt;andlt;h1andgt;Hello World!andquot;;
print andquot;
nandquot;;
printf("Content-type:text/htmlnn");
gcc simplest.c -o simplest.cgi
#include

int main()
{
printf("Content-type:text/htmlnn");
printf("n");
printf("n");
printf("andlt;h1andgt;Hello there!andlt;/h1andgt;n");
printf("n");
printf("n");
return 0;
}


andlt;h1andgt;Hello there!andlt;/h1andgt;
HTML表单
我们已了解到CGI脚本的创建过程相当简单。Web服务器执行放置在cgi-bin目录中的任何可执行程序,并且可执行程序发送给 stdout的任何输出都将出现在调用该脚本的浏览器中。现在,我们需要找到一种用于将输入发送到脚本的方法。发送输入的一般方法是使用HTML表单。 
表单在网络上随处可见。可以接受内容输入的任何页面都是表单。您可以在搜索引擎、留言簿、问卷等上面看到这些表单。您在HTML页上创建表单,并在表单的HTML标记中指定CGI脚本的名称,当用户单击表单上的Submit按钮时将调用该脚本。用户输入到表单中的值将会进行打包并发送到CGI脚本,这样脚本就可以随意使用这些值。 
实际上,上述情况是很常见的,只是您可能没有意识到而已。例如,转到 http://www.lycos.com,在“Search for:”框中键入“test”一词并按“Go Get It!”按钮。结果页的 URL 类似于下面这样: 
您会发现Lycos主页就是一个表单。Lycos在cgi-bin目录中有一个名为pursuit的脚本。表单会向此脚本发送五个参数: 
    matchmode=and cat=lycos query=test x=10 y=9
第三个参数正是我们输入的搜索字符串。其他四个参数也为脚本提供了一些信息。CGI脚本在Lycos数据库中查询“test”一词,然后返回结果。这就是任何搜索引擎的核心! 
让我们创建一个简单的表单进行试验。创建一个名为simpleform.htm的文件,然后在其中输入以下 HTML: 
可以单击此URL进行试验:http://www.howstuffworks.com/simpleform.htm。 
您可以看到,HTML代码指定创建一个使用GET方法的表单,并将该方法发送给http://www.howstuffworks.com/cgi-bin/simpleform.cgi上的CGI脚本。该表单内包含一个文本输入区域以及标准的“Submit”和“Reset”按钮。 
该表单引用的文件http://www.howstuffworks.com/cgi-bin/simpleform.cgi是一个C程序。它开始是作为一段C代码放置在名为simpleform.c的文件中: 
使用以下命令对这段代码进行编译: 
然后,将其放置在cgi-bin目录中。此程序只是收集表单发送的值并显示出来。例如,您可能会看到以下内容: 
Name是表单中的文本输入字段的标识符(表单上的每个输入字段都应具有唯一的标识符),John+Smith是可能在表单上输入的典型姓名。请注意,“+”将替换空白字符。 
通过本示例,您可以看出设置表单并将表单中的数据发送到CGI脚本中的基本过程是相当简单的。有几个细节需要注意: 
表单上的每个输入字段应具有唯一的标识符。 表单需要使用GET或POST方法。使用GET方法的好处是,您可以在发送给脚本的URL中查看表单的值,从而使调试更简单。 由于对可以通过GET方法发送的字符数目存在一定的限制,因此对于大型表单,应优先使用POST方法。 使用GET方法发送的数据可以通过查看QUERY_STRING环境变量(通常使用 C 语言程序中的getenv函数和PERL中的$ENV工具读取)接收。使用POST方法发送的数据可以通过STDIN(使用C语言程序中的gets或PERL中的 ead)获取。 发送的数据会将所有字段串接在一个字符串中,并将替换许多字符,因此需要进行转换。例如,所有空白将替换为加号。
提到QUERY_STRING环境变量,有必要在此大致介绍一下环境变量。您可以在CGI 脚本中看到大量环境变量,其中包括: 
AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE HTTP_ACCEPT HTTP_USER_AGENT PATH_INFO PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
这些环境变量中隐藏了许多有用的信息,其中包括输入字符串的长度 (CONTENT_LENGTH)、使用的METHOD(GET 或 POST -- REQUEST_METHOD可让您清楚是在STDIN 中还是在QUERY_STRING中查找输入)、用户计算机的IP地址 (REMOTE_ADDR),等等。有关这些变量的完整说明,请参阅CGI环境变量。 
    The value entered was:Name=John+Smith
    gcc simpleform.c -o simpleform.cgi
#include #include int main(){  printf("Content-type:text/htmlnn");  printf("n");  printf("n");  printf("            The value entered was:")
printf("%sn", getenv("QUERY_STRING"));
printf("n");
printf("n");
return 0;
}
A super-simple form
Enter Your Name:
http://www.lycos.com/cgi-bin/pursuit?matchmode=and andcat=lycosandquery
创建真实表单
一个真实的表单将包含各种输入区域,并需要在脚本中编写一些代码来撤消字符映射并分析出单个字符串。让我们先看看表单上的标准输入控件。这些控件包括: 单行文本输入 多行文本输入 选择列表 复选框 单选按钮 专用按钮 - 用于提交或清除表单 正如在任何其他网页上一样,你可以将这些控件与其他静态文本和图形组合在一起。 

以下的一些示例演示了如何使用不同的控件标记:

单行编辑
“input”一词标识单行编辑区域。“name”字段为控件提供了一个用于 CGI 脚本的标识符,此标识符对于表单上的每个控件都应是唯一的。“size”字段指示表单上的输入区域的宽度(以字符为单位);“Maxlength”对输入区域内的最大字符数进行限制。“value”设置初始值。

Enter Name:<input name=andquot;Nameandquot; size=30 maxlength=50value=andquot;Sampleandquot;>

通常,输入区域的前面是一段静态文本,用于标识输入字段的用途。此处显示的是静态文本“Enter Name:”。

可以添加值“type=int”,以便将输入限制为整数值。默认情况下,值的类型为“text”,可接受任何字符。

多行编辑
多行编辑区域与输入区域类似。你可定义该控件的名称,并定义其在表单上的大小(以行和列为单位)。你在 标记之前输入的任何内容都将作为默认值出现在控件中。

<textarea name=andquot;Company Addressandquot; cols=30rows=4></textarea>

复选框
复选框是类型设置为“checkbox”的特殊形式的输入区域。

<input type=checkbox name=andquot;Includeandquot; value=1>

如果选中复选框,则将返回值。

单选按钮
单选按钮与复选框类似,但它们可以组合在一起呈现:

Choose the search area:<br><input type=radio CHECKED name=universe value=US-STOCK>Stocks<input type=radio name=universe value=CA-STOCK>Canadian Stocks<input type=radio name=universe value=MMF>Money Markets<input type=radio name=universe value=MUTUAL>Mutual Funds

请注意,可以用 CHECKED 一词来标记默认的单选按钮。还请注意,同一组中的所有单选按钮的名称是相同的。

选择列表
选择列表使用户可以在许多选项中进行选择。选择列表的标记可让你在“size”字段中指定可见行的数目,并可让你指定所有选项的值。

Select an Option<br><SELECT size=2 NAME=andquot;Optionandquot;>    <OPTION> Option 1    <OPTION> Option 2    <OPTION> Option 3    <OPTION> Option 4</SELECT>

MULTIPLE 一词将创建多选功能。

专用按钮
下列标记将创建两个专用按钮,一个按钮用于向服务器提交表单,另一个按钮用于重置表单:

<INPUT TYPE=submit value=andquot;Submitandquot;><INPUT TYPE=reset value=andquot;Resetandquot;>
示例:创建网页问卷
假设您要为某个网页创建一份简单的问卷。例如,您想要询问读者的姓名、性别、年龄和意见,然后在CGI脚本中进行处理。HTML表单可能存在于一个名为http://www.bowenwang.com.cn/survey.htm的文件中,形式如下: 
<html>
<body>
<h1>BWW Survey Form<h1>
<FORM METHOD=POST ACTION="http:
//www.bowenwang.com.cn/cgi-bin/survey.cgi">
Enter Your Name:
<input name="Name" size=20 maxlength=50>
<P>Enter your sex:
<input type=radio CHECKED name=sex value=MALE>Male
<input type=radio name=sex value=FEMALE>Female
<P>Select your age<br>
<SELECT size=2 NAME=age>
<OPTION> 1-10
<OPTION> 11-20
<OPTION> 21-30
<OPTION> 31-40
<OPTION> 41-50
<OPTION> 51-60
<OPTION> 61 and up
</SELECT>
<P>Enter Your Comment:
<input name="Name" size=40 maxlength=100>
<P>
<INPUT TYPE=submit value="Submit">
<INPUT TYPE=reset value="Reset">
</FORM>
</body>
</html>
此表单引用的CGI脚本将接收四条不同的数据:提交表单的读者的姓名、年龄、性别和意见。脚本将对这四个值进行分析并处理所有字符转换。文件http://www.bowenwang.com.cn/survey.c用于创建脚本survey.cgi,其长度可能为100行。
关于CGI的一些知识
在这个关于CGI脚本的简明教程中,我们已了解到: CGI脚本是一个程序——通常为C语言程序或PERL脚本。 

在大多数服务器上,CGI脚本位于一个名为cgi-bin的目录中。当浏览器请求脚本的URL时,将执行该脚本。

脚本发送给STDOUT的任何内容都将被发送给浏览器。首先发送字符串“Content-type: text/htmlnn”。然后,再发送其他内容;但是一般来说,将发送有效HTML文档的有效HTML标记。

通过创建HTML表单并使用其中的 ACTION指定脚本的URL,可将输入内容发送给脚本。

当脚本接收到表单中的数据后,需要分析出不同的字符串并转换所有修改过的字符。我们演示了一个可以执行这些任务的简单C语言程序。PERL的CGI库(参见下一页)使PERL脚本可以轻松转换。 如果您在真实的网站上这样做,那么通常会将每次调查的结果存储在一个文本文件或数据库中,以便日后查看。利用C语言程序或PERL脚本很容易做到这一点。

 
  
  
  
  
 
更多>同类技术
 
全年征稿 / 资讯合作
 
推荐图文
推荐技术
可能喜欢