这篇文章是总结和翻译来的,为了让完全不熟悉的人可以先有可运行的代码执行学习。
关键词:apache服务器,用C写cgi,基本,入门,代码。
apache搭建:
http://apache.etoak.com/httpd/
我用的是这个版本:http://apache.etoak.com/httpd/httpd-2.2.19.tar.gz
tar->configure->make.安装在/usr/share/apache2
./configure –prefix=/usr/share/apache2 –enable-so –enable-mods-shared=all –enable-rewrite –enable-cache
配置apache.修改配置文件
/usr/share/apache2/conf/httpd.conf
其中两句为:
DocumentRoot “/usr/share/apache2/htdocs”
ScriptAlias /cgi-bin/ “/usr/share/apache2/cgi-bin/”
DocumentRoot为HTML文件的目录。
ScriptAlias后面是指定/cgi-bin/连接到/var/www/html/cgi-bin/ ,此目录中的文件均被认作是cgi程序。
第一个例子:最简单的cgi实现:
写一个最简单的hello world.
#include <stdio.h>int main()
{
printf("Content-Type: text/htmlnn");
printf("Hello, worldn");
return 0;
}
#sudo gcc hello.c -o hello.cgi
hello.cgi文件放在/usr/share/apache2/cgi-bin/中。
http://127.0.0.1/cgi-bin/hello.cgi 就会显示出helloworld了。
第二个例子:通过html调用cgi的一个最简单例子:
<html>
<head>hello cgi.</head>
<body>
<form action="/cgi-bin/hello.cgi" method="post">
<input type="submit" value="ok">
</form>
</body>
</html>
将此html文件放在/usr/share/apache2/htdocs中。
http://127.0.0.1/hello.htm 点按钮。
提交按钮(input type=”submit”)用于向服务器发送表单数据。数据会发送到表单的 action 属性中指定的页面。 填写表单(form)并点击发送按钮(submit),表单的内容就被发送到服务器端由cgi进行处理或其他操作。
什么是CGI:
CGI(公用网关接口)规定了Web服务器调用其他可执行程序(CGI程序)的接口协议标准.Web服务器通过调用CGI程序实现和Web浏览器的交互,也就是CGI程序接受Web浏览器发送给Web服务器的信息,进行处理,将响应结果再回送给Web服务器及Web浏览器。CGI程序一般完成Web网页中表单(Form)数据的处理、数据库查询和实现与传统应用系统的集成等工作。
简而言之:CGI是用来沟通HTML表单(form)和服务器端程序(CGI程序)的接口(interface).
CGI接口标准
包括标准输入、环境变量、标准输出三部分。
1.标准输入
CGI程序可通过标准输入(stdin)从Web服务器得到输入信息,如Form中的数据,这就是所谓的向CGI程序传递数据的POST方法。这意味着在操作系统命令行状态可执行CGI程序,对CGI程序进行调试。
2.环境变量
Web服务器和CGI接口设置了环境变量,用来向CGI程序传递一些重要的参数。CGI的GET方法还通过环境变量QUERY-STRING向CGI程序传递Form中的数据。
3.标准输出
CGI程序通过标准输出(stdout)将输出信息传送给Web服务器。传送给Web服务器的信息可以用各种格式,通常是以纯文本或者HTML文本的形式,这样我们就可以在命令行状态调试CGI程序,并且得到它们的输出。
from的get与post
HTML中form元素用method属性来指定有两种不同的提交方法,即”get”和”post”。
使用get时,form的数据集(形如control-name=current-value的键值对)被附加到form元素的action属性所指定的URI后面;
使用post时,form的数据集(形如control-name=current-value的键值对)被包装在HTTP Request 的 body 中 并被发送。
form中的get和post方法,在数据传输过程中分别对应了HTTP协议中的GET和POST方法。二者主要区别如下:
1、 Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据。
2、 Get将表单中数据的按照variable=value的形式,添加到action所指向的URL后面,并且两者使用“?”连接,而各个变量之间使用 “&”连接;Post是将表单中的数据放在form的数据体中,按照变量和值相对应的方式,传递到action所指向URL。
3、 Get是不安全的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。
4、 Get传输的数据量小,这主要是因为受URL长度限制
5、 编码区别:Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。
6、 Get是Form的默认方法。
7、 使用Get方法时,从系统变量中获得数据;使用Post方法时,从标准输入中获得。
备注:1.Get是用来从服务器上获得数据,而Post是用来向服务器上传递数据。
官方建议:当且仅当表单处理是”idempotent”时使用Get方法。然而,在某些情况下(长url或非ASCII编码),即使是”idempotent”的表单处理,也必须使用Post方法。
what is idempostent:(懒得翻译了.)
Idempotent processing does not exclude fundamental changes, only that processing the same data twice has the same effect as processing it once.
In fact, idempotent processing means that a form submission causes no changes anywhere except on the user’s screen (or, more generally speaking, in the user agent’s state). Thus, it is basically for retrieving data. If such a form is resubmitted, it might get different data (if the data had been changed meanwhile), but the submission would not cause any update of data or other events. The concept of changes should not be taken too pedantically; for instance, it can hardly be regarded as a change that a form submission is logged into the server’s log file. On the other hand, sending E-mail should normally be regarded as “an effect on the state of the world”.
If the processing of a form is idempotent (i.e. it has no lasting observable effect on the state of the world), then the form method should be GET. Many database searches have no visible side-effects and make ideal applications of query forms.
If the service associated with the processing of a form has side effects (for example, modification of a database or subscription to a service), the method should be POST.
GET requests should always be idempotent on the server. This means that whereas one GET request might (rarely) change some state on the Server, two or more identical requests will have no further effect.
后注:
ajax基础教程居然用一句话搞定这个东西:
所谓幂等是指多个请求返回相同的结果。
GET表单的处理
Get方法在CGI定义为:当表单被发送到服务器断后,表单中的数据被保存在服务器上一个叫做QUERY_STRING的环境变量中。这种表单的处理相对简单,只要读取环境变量就可以了。在CGI程序中的标准输出(output)经过重定义了的。它并没有在服务器上产生任何的输出内容,而是被重定向到客户浏览器。这样,如果编写一个C的CGI程序的时候,把一个HTML文档输出到它的stdout上,这个HTML文档会被在客户端的浏览器中显示出来。这也是CGI程序的一个基本原理。
mul.htm
<form action="/cgi-bin/mul.cgi" method="get">
<input name="m" size="10">
*
<input name="n" size="10"></br>
<input type="submit" value="ok">
</form>
#mul.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *data;
long m, n;
printf("Content-Type:text/html;charset=gb2312nn");
printf("<title>result</title>");
data = getenv("QUERY_STRING");
if (NULL == data)
printf("<p>Error.</p>");
else if (sscanf(data, "m=%ld&n=%ld", &m, &n)!=2)
printf("<p>Please input number.</p>");
else
printf("<p>Result: %ld * %ld = %ld.</p>", m, n, m*n);
return 0;
}
POST表单的处理
当用户提交一个HTML Form时,Web浏览器首先对Form中的数据以名字/值对的形式进行编码,并发送给Web服务器,然后由Web服务器传递给CGI程序。格式: name1=value1&name2=value2&name3=value3&name4=value4&…
其中名字是Form中定义的INPUT、SELECT或TExtAREA等标置(Tag)名字,值是用户输入或选择的标置值。这种格式即为URL编码,程序中需要对其进行分析和解码。要分析这种数据流,CGI程序必须首先将数据流分解成一组组的名字/值对。这可以通过在输入流中查找下面的两个字符来完成。 每当找到字符=,标志着一个Form变量名字的结束;每当找到字符& ,标志着一个Form变量值的结束。请注意输入数据的最后一个变量的值不以&结束。
一旦名字/值对分解后,还必须将输入中的一些特殊字符转换成相应的ASCII字符。这些特殊字符是:
+:将+转换成空格符;
%xx:用其十六进制ASCII码值表示的特殊字符。根据值xx将其转换成相应的ASCII字符。
对Form变量名和变量值都要进行这种转换。
collect.htm
<form action="/cgi-bin/collect.cgi" method="post"><p>input data: </br>
<input name="data" size="60" maxlength="80"></br>
<input type="submit" value="ok">
</form>
#collect.c
#include <stdio.h>
#include <stdlib.h>
#define MAXLEN 80
#define EXTRA 5 /* 4个字节留给字段的名字"data", 1个字节留给"=" */
#define MAXINPUT (MAXLEN+EXTRA+2) /* 1个字节留给换行符,还有一个留给后面的NULL */
#define DATAFILE "data.txt"
void unencode(char* src, char* last, char* dest)
{
for (; src!=last; src++, dest++)
{
if (*src == '+')
*dest = ' ';
else if (*src == '%')
{
int code;
if (sscanf(src+1, "%2x", &code) != 1)
code = '?';
*dest = code;
src += 2;
}
else
*dest = *src;
}
*dest = 'n';
*++dest = '';
}
int main(void)
{
char *lenstr;
char input[MAXINPUT], data[MAXINPUT];
long len;
printf("Content-Type:text/html;charset=gb2312nn");
printf("<title>Response</title>");
lenstr = getenv("CONTENT_LENGTH");
puts(lenstr);
if(lenstr == NULL || sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)
printf("<p>Error.");
else
{
FILE *f;
fgets(input, len+1, stdin);
unencode(input+EXTRA, input+len, data);
f = fopen(DATAFILE, "a+");
if(f == NULL)
printf("<p>Error.");
else
fputs(data, f);
fclose(f);
printf("<p>data saved: %s",data);
}
return 0;
}
#view.c
#include <stdio.h>
#include <stdlib.h>
#define DATAFILE "data.txt"
int main(void)
{
FILE *f = fopen(DATAFILE, "r");
printf("Content-Type:text/html;charset=gb2312nn");
int ch;
if (f == NULL)
printf("<p><em>Error: cannot open file.</em>");
else
{
while ((ch=getc(f)) != EOF)
putchar(ch);
fclose(f);
}
return 0;
}
注意的是:这个data.txt的文件权限.
chmod 666 data.txt
CGI程序和一般的程序有所不同,一般的程序在读完了一个文件流的内容之后,会得到一个EOF的标志。但在CGI程序的表单处理过程中,EOF是永远不会出现的,所以千万不要读多于CONTENT_LENGTH长度的字符,否这会有什么后果,谁也不知道(CGI规范中没有定义,一般根据服务器不同而有不同得处理方法)。
EOF.