@Jan 9, 2014
1 CGI是什么
CGI(Common GatewayInterface)是外部应用与WWW服务器交互的一个标准接口。简单来说,CGI 是一个标准,它定义了浏览器和服务器如何交互,比如浏览器如何向服务器发送消息,而服务器收到消息后又如何进行处理。
既然是一个标准,只要遵循CGI的规范,CGI程序可以用任何语言来编写,如C, C++, VB。Perl语言因其强大的字符串处理能力一度成为编写CGI程序的首选,现在Python语言因在很多方面优于Perl,更是非常适合编写CGI程序。
1.1 工作原理
在服务器上,运行产着一个守护进程对端口进行监听,等待来自客户的请求。当一个请求到来时,将会创建一个子进程为用户的连接服务。根据请求的内容,服务器返回HTML文件或者通过CGI调用外部应用程序并返回处理结果。
1.2 一个CGI程序
在陷入细节之前,一个简单的示例非常有助于理解CGI程序如何工作。
下面是一个非常简单的HelloWorld程序,用C语言实现,浏览器直接请求一个cgi程序获取“hello world”。客户端的操作就是在浏览器输入下面的地址请求内容:
http://127.0.0.1/cgi-bin/helloworld.cgi
服务器部署一个HelloWorld.cgi负责执行。
HelloWorld.c
#include <stdio.h>
int main(void)
{
printf("\n"); //这个必须有
printf("Hello, World!\n");
return0;
}
编译后将创建的HelloWorld.exe重命名为HelloWorld.cgi放到服务器的相应目录下。
当用户在浏览器中输入上面提到的地址后,服务器会调用HelloWorld.cgi程序并将结果返回给浏览器显示出来。
2 CGI程序编程
浏览器向服务器发送数据时采用编码的形式进行,就是CGI编码。比如下面一个用户填写的用来登录的表单数据,这些数据的名值对会根据它们在表单中的顺序通过“&”连在一起。
Login的表单:
<form method="POST"enctype="multipart/form-data" action="./login.cgi">
用户名: <input type="text" name="user_name"/><br>
密码: <input type="text" name="password"/></br>
<input type="submit"name="login" value="登录">
</form>
URL编码规则:
- 变量之间用“&”分开;
- 变量名与变量值用“=”连接;
- 空格用“+”代替;
- 保留的控制字符和特殊字符则用“%”连接对应16进制的ASCII码代替;
- 空格、任意不可打印的ASCII和控制字符均为非法字符。
服务器端CGI程序获取客户端发送的数据有三种途径:环境变量、命令行和标准输入。具体使用哪一种方式根据form的method属性而定。
2.1 Get方法
Get方法通过附加在URL后面的参数发送请求信息,发送的数据长度一般不能超过1024,否则就得使用Post方法。到了服务器后这些信息将被放在环境变量QUERY_STRING中传给CGI程序。当从服务器获取数据而不改变服务器上的数据时,应该选用GET方法。
2.2 Post方法
当浏览器将数据从一个填写的表单传给服务器时一般采用POST方法。当使用POST方法时,Web服务器向CGI程序的标准输入STDIN传送数据。发送的数据长度存在环境变量 CONTENT_LENGTH中。Post方法的数据格式为:
variable1=value1&variable2=value2&etc
CGI程序通过检查REQUEST_METHOD环境变量以确定是否采用了POST方法,从而从标准输入读取数据。
2.3 环境变量
环境变量是一个保存信息的内存区。对于CGI程序来说,它继承了系统的环境变量。CGI环境变量在CGI程序启动时初始化,在结束时销毁。
当一个CGI程序不是被HTTP服务器调用时,它的环境变量几乎是系统环境变量的复制。 当这个CGI程序被HTTP服务器调用时,它的环境变量就会多了HTTP服务器、客户端、请求等相关的内容,如下面与请求相关的变量:
l REQUEST_METHOD,
l QUERY_STRING,客户端采用Get方法发送的数据
l CONTENT_LENGTH ,STDIO中的有效信息长度
2.4 获取数据
当客户通过浏览器发出CGI请求时,服务器在调用CGI程序时,服务器把该用户的信息保存到环境变量里。
CGI程序通过读取环境变量REQUEST_METHOD的值来确定通过何种方式获取请求数据。
下面的代码演示了这一过程:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int get_inputs() {
int length;
char *method;
char *inputstring;
method = getenv(“REQUEST_METHOD”);
if (method == NULL)
return -1;
if (!strcmp(method, ”POST”)) {
length = atoi(getenv(“CONTENT_LENGTH”));
if (length != 0) {
inputstring = malloc(sizeof(char) * length + 1)
//必须申请缓存,因为stdin是不带缓存的。
fread(inputstring, sizeof(char), length, stdin);
}
}
elseif (!strcmp(method, “GET”)) {
Inputstring = getenv(“QUERY_STRING”);
length = strlen(inputstring);
}
return length;
}
使用数据需要对得到的字符串进行解码,也就是把“value=1&value2=2”这样的字符串转换为可以使用的单个变量。
现在一般都有成熟的库来做这些事情,只需要调用相关的api就可以了。
2.5 返回结果
上面已经看到过,CGI程序通过stdout将信息处理结果返回给客户端,这个输出需要符合CGI格式。
int main(void)
{
printf("\n"); //这个必须有
printf("Hello, World!\n");
return0;
}
在CGI程序中的标准输出stdout是经过重定义了的,它并没有在服务器上产生任何的输出内容,而是被重定向到客户浏览器,这与它是由C,还是Perl或Python实现无关,如C使用printf向该进程的标准输出发送数据,Perl和Python用print向该进程的标准输出发送数据。所以我们可以用打印来实现客户端新的HTML页面的生成。
CGI的格式输出内容必须组织成标题/内容的形式。CGI标准规定了CGI程序可以使用的三个HTTP标题。标题必须占据第一行输出!而且必须随后带有一个空行。
2.6 CGI的C语言库
前面提到有现成CGI库来做数据获取、解析和输出工作,如CgiHtml和CGIC。
Cgihtml是一个应用非常广泛的C语言编写的CGI库。它提供的功能函数如下:
- Read_cgi_input():获取并解析HTML表单输入,返回一个指向某结构体的指针
- Cgi_val():获取每个表单变量的值
- Html_header():输出HTML标题栏
- Html_begin():输出HTML文档的开始部分
- H1():输出一行字符,字体为H1
- Html_end():输出HTML文档的结尾部分
这里使用的是CGIC,后面会继续介绍。
后续见