@Jan 9, 2014
前接CGI入门
1 环境搭建
这里使用的环境是OS: Win7,服务器:Apache Httpd,编程语言:C,编程工具C-Free,
Linux系统可以使用服务器Boa。
1.1 下载C-Free编译器
C-Free是一款支持多种编译器的专业化C/C++集成开发环境(IDE)。利用本软件,使用者可以轻松地编辑、编译、连接、运行、调试C/C++程序。下载地址:
http://www.programarts.com/服务器
1.1.1 下载Apache Http Server (Apache Httpd)
下载地址http://httpd.apache.org/
下载binary,打开下面win32文件夹中的某个版本,如下面的
httpd-2.0.65-win32-x86-openssl-0.9.8y.msi (带ssl)
或者httpd-2.0.65-win32-x86-no_ssl.msi (不带ssl)
带SSL表示可以配置SSL安全链接,即Https
Binary 目录
1.1.2 安装Apache Http Server
“Network Domain”填写你的网络域名,如果没有网络域名,可以随便填写。但如果你架设的 Apache 服务器如果要放入Internet ,则一定要填写正确的网络域名。在“Server Name”下填入你的服务器名,也就是主机名。“Administrator's Email Address”填写系统管理员的联系电子邮件地址。上述三条信息三条信息均可任意填写,无效的也行,其中联系电子邮件地址会在当系统故障时提供给访问者。
见http://jingyan.baidu.com/article/e4d08ffdc1ab5b0fd2f60df4.html
1.1.3 端口冲突
安装之后,我们占用80端口,或者8080端口,有时候会因为端口冲突而造成无法启动apace2.2。
需要将c:\apache\conf\httpd.conf中的配置进行更改,将其中的Listen8080 或者Listen 80,更改为 Listen 50080。
这是由于windows IIS中的Web服务器默认情况下在TCP 80端口监听连接请求,而8080端口一般留给代理服务器使用,所以为了避免Apache web 服务器的监听端口与其发生冲突,将Apache Web服务器的监听端口修改为不常用的高端端口50080。
在浏览器中进行访问时,应使用http://localhost:50080/即可。
见http://blog.163.com/xiaoyang_80/blog/static/1723721352010111392055674/
1.1.4 配置Apache Http Server
配置Apache以支持CGI程序
打开Apache安装目录下的\conf\httpd.conf文件,找到下面一句
修改apache的配置文件httpd.conf:
将下面的
<Directory "D:/Apache Group/Apache2/cgi-bin">
AllowOverride None
OptionsNone
Orderallow,deny
Allowfrom all
</Directory>
#AddHandler cgi-script .cgi
改为:
<Directory "D:/ApacheGroup/Apache2/cgi-bin">
AllowOverride None
Options ExecCGI
Orderallow,deny
Allowfrom all
</Directory>
AddHandler cgi-script .cgi .pl
注意:用C语言编写的CGI程序后缀是.cgi,Perl编写的后缀是.pl,Python编写的后缀是.py。
如果还想要修改cgi程序存放的目录,将下面的
ScriptAlias /cgi-bin/ "D:/Program Files(x86)/Apache Group/Apache2/cgi-bin/"
改为
ScriptAlias /somepath/ "D:/Program Files (x86)/ApacheGroup/Apache2/cgi-bin/"
修改请求目录时默认显示的文件
请求某个目录时,默认显示的文件是index.html , index.html.var .
要修改为其他文件,找到httpd.conf中的下面这一行:
DirectoryIndex index.html index.html.var
将上面的index.html index.html.var 修改为想要显示的其他文件的名字,如下面:
DirectoryIndexindex.html index.htm default.htm index.php index.pl
甚至可以是子目录下的
DirectoryIndexindex.html index.pl /nav/index.php
然后重启服务器
参考:Apache Display / Change a Default Page Other Than index.html.
http://www.cyberciti.biz/faq/apache-display-or-change-a-default-page-other-than-indexhtml/
1.1.5 测试
运行一个helloword的CGI程序
(参考
官方文档Dynamic content with CGI. http://httpd.apache.org/docs/2.2/howto/cgi.html)
写一个HelloWorld.c的cgi,代码如下:
#include <stdio.h>
int main(void)
{
printf("Content-Type:text/html; charset=gb2312;\n\n");//后面必须加两个回车
printf("<html>");
printf("<head><title>Hello</title></head>\n<body>\n")
printf("<h3>Hello World </h3>\n");
return0;
}
编译后成了HelloWorld.exe,重命名为HelloWorld.cgi复制到apache的cgi-bin目录下
注意:
1)用VC编译可以保证正常运行,用Turboc2编译会出现错误,无法执行,原因可能是后者为16位编译器,前者为32位编译器的缘故。
2)必须首先输出一个空行,如果不输出该空行,则会出现题为“Internal Server Error”的错误,所以第一行的printf("\n");这个必须有。
运行程序时,打开网页,在地址栏中输入下面的网址:
http://127.0.0.1/cgi-bin/helloworld.cgi
浏览网页看结果。
2 CGIC库的使用
所有的CGI程序都必须执行一些特定的工作,如解析表单数据,CGIC库提供了这样的一些API。
2.1 下载CGIC
从以下网址下载CGIC库:
www.boutell.com/cgic/#obtain
2.2 创建一个sample application
CGIC库自带了一个程序cgitest.c,
参考
CGIC简明教程目录(CGI篇)
http://wenku.baidu.com/view/3c8bb1d126fff705cc170aac.html
创建一个cgitest工程,包含cgitest.c, cgi.c, cgi.h三个文件,编译后将重名为.cgi并拷贝到apache的cgi-bin目录下,用http://localhost/cig-bin/cgitest.cgi进行测试。
这是一个表但提交程序,提交后会显示提交的内容:
Cgitest界面
2.3 编写使用CGIC库的程序
CGIC库提供了自己的main()函数。使用该库时,不需要写main函数,而是要写一个cgiMain()函数,该函数会在初始cgi工作完成后被激活。程序需要include头文件cgi.h文件。如果要修改默认的main函数的行为可以修改cgi.c中的main()函数。
2.4 获取URL参数
通过JavaScript提交表单时,参数可以跟在url后面,这时需要将方法设为GET,然后使用CGIC的API获取这些参数。
调试时,为了检查传入的参数是否正确,可以在CGIC程序里引入下面的变量,然后查看该变量即可,该变量包含了URL带的原始参数。
…
#include “cgic.h”
extern char *cgiQueryString;
int main() {…}
3 Errors Shooting
3.1 502 Bad Gateway The CGI was notCGI/1/1 compliant
访问Ubuntu下BOA中的CGI程序,提示” 502 Bad Gateway The CGI was not CGI/1/1 compliant”,
Server log显示 “cgi-header:unable to find LFLF”。
没有成功执行CGI程序,这可能是由于CGI程序不存在或权限不够造成,如果是权限不够,赋予该cgi执行权限:chmod 777 file_name.cgi
参考:
CGI和BOA使用期间遇到的问题汇总(转).
http://www.cnblogs.com/hnrainll/archive/2011/06/01/2067295.html
3.2 400 Bad Request
CGI程序在Windows下的 ApacheServer中运行正确,布署到Ubuntu虚拟机的BOA Server后,访问CGI程序,返回的response(request.responseText,使用Ajax发送POST的异步请求)提示”400 Bad request”错误。
Server Log提示错误:”InvalidContent-Length [0] on POST!”,在Ubuntu虚拟机中访问和Windows(IE和Firefox)访问结果一样。
请求方法改为”GET”后OK。
其他相关错误
http://stackoverflow.com/questions/328281/why-content-length-0-in-post-requests
Invalid Content-LengthHeader May Cause Requests to Fail Through ISA Server.
http://support.microsoft.com/kb/300707 该文的错误原因是由于某些浏览器加了两个字符(回车和换行)在消息体的末尾,但却没有在HTTP request的content-length中体现这两个字符,导致实际内容长度大于content-length。
4 附
4.1 CGI环境变量
与请求相关的环境变量 | REQUEST_METHOD | 服务器与CGI程序之间的信息传输方式 |
QUERY_STRING | 采用GET时所传输的信息 | |
CONTENT_LENGTH | STDIO中的有效信息长度 | |
CONTENT_TYPE | 指示所传来的信息的MIME类型 | |
CONTENT_FILE | 使用Windows HTTPd/WinCGI标准时,用来传送数据的文件名 | |
PATH_INFO | 路径信息 | |
PATH_TRANSLATED | CGI程序的完整路径名 | |
SCRIPT_NAME | 所调用的CGI程序的名字 | |
与服务器相关的环境变量 | GATEWAY_INTERFACE | 服务器所实现的CGI版本 |
SERVER_NAME | 服务器的IP或名字 | |
SERVER_PORT | 主机的端口号 | |
SERVER_SOFTWARE | 调用CGI程序的HTTP服务器的名称和版本号 | |
与客户端相关的环境变量 | REMOTE_ADDR | 客户机的主机名 |
REMOTE_HOST | 客户机的IP地址 | |
ACCEPT | 例出能被次请求接受的应答方式 | |
ACCEPT_ENCODING | 列出客户机支持的编码方式 | |
ACCEPT_LANGUAGE | 表明客户机可接受语言的ISO代码 | |
AUTORIZATION | 表明被证实了的用户 | |
FORM | 列出客户机的EMAIL地址 | |
IF_MODIFIED_SINGCE | 当用get方式请求并且只有当文档比指定日期更早时才返回数据 | |
PRAGMA | 设定将来要用到的服务器代理 | |
REFFERER | 指出连接到当前文档的文档的URL | |
USER_AGENT | 客户端浏览器的信息 |
CONTENT_TYPE:如application/x-www-form-urlencoded,表示数据来自HTML表单,并且经过了URL编码。
ACCEPT:客户机所支持的MIME类型清单,内容如:”image/gif,image/jpeg”
REQUEST_METHOD:它的值一般包括两种:POST和GET,但我们写CGI程序时,最后还要考虑其他的情况。
4.2 解析数据
/*function getPOSTvars*/
char **getPOSTvars(){
int i;
int content_length;
char **postvars;
char *postinput;
char **pairlist;
int paircount=0;
chr*nvpair;
char *eqpos;
postinput=getenv("CONTENT_LENGTH");//获取传送给程序数据的字节数
if(!postinput)
exit();
if(!content_length=atoi(postinput))) //获取信息长度¨
exit(1);
if(!(postinput=(char*)malloc(content_length+1)))
exit(1);
if(!fread(postinput,content_length,1,stadin))
exit(1);
postinput[content_length]='0';
for(i=0;postinput[i];i++)
if(postinput[i]=='+')
postinput[i]=''; //对加易进行处理
pairlist=(char **)malloc(256*sizeof(char **));
paircount=0;
nvpair=strtok(postinput,"&"); //从出现“&”字符的位置把信息分段,然后对结果依次处理
while (nvpair){
pairlist[paircount++]=strdup(nvpair);
if(!(paircount%256))
pairlist=(char**)realloc(pairlist,(paircount+256)*sizeof(char**));
nvpair=strtok(NULL,"&");
}
pairlist[paircount]=0;
postvars=(char**)malloc((paircount*2+1)*sizeof(char **));
for(i=0;i<paircount;i++){
if(eqpos=strchr(pairlist[i],'=')){
*eqpos='0';
unescape_url(postvars[i*2+1]=strdup(eqpos+1));//调用unescape_url函数继续解码
}else{
unescape_url(postvars[i*2+1])=strdup(""));
}
postvars[paircount*2]=0;
for(i=0;pairlist[i];i++)
free(pairlist[i]);
free(pairlist);
free(postinput);
return postvars;
}
5 参考
CGI编程完全手册(个人学习笔记)
http://wenku.baidu.com/view/11eb19f90242a8956bece4c2.html
apache的httpd.conf中文详解(转)
http://blog.linuxphp.org/archives/918/
让apache支持包含shtml,html,htm类型文件
http://www.woaidiannao.com/dnzs/17856.html
让apache支持包含shtml,html,htm类型文件(非原创)