粗谈CGI
先看看 维基百科上面关于 CGI的介绍http://zh.wikipedia.org/wiki/%E9%80%9A%E7%94%A8%E7%BD%91%E5%85%B3%E6%8E%A5%E5%8F%A3
一般我们在开发Web运用的时候很少接触CGI,这种底层的处理细节。但如果你想彻底理解Resquest-Response 过程,自己编写运用服务器就有必要详细了解CGI,很多语言的动态网页技术都是基于CGI的思想,对CGI进行扩展,比如Python的WSGI,Perl的PSGI。
有一篇介绍CGI很好的文章 http://www.jdon.com/idea/cgi.htm
我们知道HTTP Server只能处理用户的静态请求,但是如果用户的一个请求,请求的数据需要从数据库里面获取怎么办(我们称之为动态请求)。
我们在编写某个Web运用系统的时候,我们不使用任何任何HTTP服务器,我们使用最原始的Socket编程,我们编写程序自己解析用户的请求(HTTP协议),如果是静态文件,我们直接调用处理静态文件的方法,如果是动态请求我们则调用处理相应动态请求的方法,在整个过程中除了处理HTTP协议,我们没有使用任何其他协议,这种处理很直接,过程也很清楚。但是如果系统要增加新的功能,我们则要添加或修改对应的方法,很不容易扩展和维护。如果按照这种方式去编写一个Web网站,那么对程序员的要求就特别高,不仅要精通Socket编程和HTTP协议,还要会HTML编程。
下面是图示和为代码
while true:
conn = server.accept() #接受连接
req = conn.read() #读取用去请求
headers = parse_http(reqr) #解析用户请求
if is_static(headers[url]): #如果是静态请求
res_str = do_static(req_file) else if is_dynamic(headers[url]): #如果是动态请求
res_str = do_dynamic(req) res_str = end_handler(res_str) #对响应的字符串进行加工
conn.write(res_str) #向用户输出相应的结果
conn.close() #关闭连接
按照上面那种方式的话,我们每编写一个Web系统都要重复以上步骤。而且整个处理动态请求的方法要用同一种语言。所以这种可行但不适用。其实按照上面这种 思路的话编写的就是一个运用系统了。
思路没变,我们把重复的步骤提取出了,也就是除了动态处理的所有步骤,构成了一个HTTP Server。如果我们要开发一个网站系统,只用编写相应的HTTP的静态文件,和编写处理动态请求的脚本。我们称之为CGI脚本,CGI脚本可以由任何语言编写。 我们知道大部分高性能的HTTP Server 都是用C/C++编写的,如果HTTP Server要调用的CGI脚本,调用CGI脚本我们可以使用Unix中的 execve 系统,下面是一些execve系统的一些函数及其用法。
函数名: exec...
功 能: 装入并运行其它程序的函数
用 法: int execl(char *pathname, char *arg0, arg1, ..., argn, NULL);
int execle(char *pathname, char *arg0, arg1, ..., argn, NULL,
char *envp[]);
int execlp(char *pathname, char *arg0, arg1, .., NULL);
int execple(char *pathname, char *arg0, arg1, ..., NULL,
char *envp[]);
int execv(char *pathname, char *argv[]);
int execve(char *pathname, char *argv[], char *envp[]);
int execvp(char *pathname, char *argv[]);
int execvpe(char *pathname, char *argv[], char *envp[]);
程序例: /* execv example */
#include <process.h>
#include <stdio.h>
#include <errno.h> void main(int argc, char *argv[])
{
int i; printf("Command line arguments:\n");
for (i=; i<argc; i++)
printf("[%2d] : %s\n", i, argv[i]); printf("About to exec child with arg1 arg2 ...\n");
execv("CHILD.EXE", argv); perror("exec error"); exit();
}
但是我们调用CGI脚本的时候要使用到一些请求的参数信息吧。在CGI里面有一个重要的名称 Environment variables 环境变量,下面列出 CGI/1.1 定义的环境变量
Environment variables Environment variables are used to pass data about the request from
the server to the script. They are accessed by the script in a system
defined manner. In all cases, a missing environment variable is
equivalent to a zero-length (NULL) value, and vice versa. The
representation of the characters in the environment variables is
system defined. Case is not significant in the names, in that there cannot be two
different variable whose names differ in case only. Here they are
shown using a canonical representation of capitals plus underscore
("_"). The actual representation of the names is system defined; for
a particular system the representation may be defined differently to
this. The variables are: AUTH_TYPE
CONTENT_LENGTH
CONTENT_TYPE
GATEWAY_INTERFACE
HTTP_*
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
从字面上大家应该看出来所代表的含义吧,是不是有点类似 HTTP headers 。讲到这里大家应该很清楚 CGI 的处理过程了吧。总结来说当Web服务器接受CGI请求时,服务器将设置一些CGI程序的环境变量,运行CGI脚本时,CGI脚本在从环境变量中获取感兴趣的变量(比如获取 查询字符串 QUERY_STRING),进行处理,响应结果。至于如何设置和获取环境变量请查看 详解 Unix环境变量。
在列出一段 HTTP Server处理CGI请求的源码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h> main(int ac, char *av[])
{
int sock, fd;
FILE *fpin;
char request[BUFSIZ]; if ( ac == ){
fprintf(stderr,"usage: ws portnum\n");
exit();
}
sock = make_server_socket( atoi(av[]) );
if ( sock == - ) exit(); /* main loop here */ while(){
/* take a call and buffer it */
fd = accept( sock, NULL, NULL );
fpin = fdopen(fd, "r" ); /* read request */
fgets(request,BUFSIZ,fpin);
printf("got a call: request = %s", request);
read_til_crnl(fpin); /* do what client asks */
process_rq(request, fd); fclose(fpin);
}
} /* ------------------------------------------------------ *
read_til_crnl(FILE *)
skip over all request info until a CRNL is seen
------------------------------------------------------ */ read_til_crnl(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != )
;
} /* ------------------------------------------------------ *
process_rq( char *rq, int fd )
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GET /foo/bar.html HTTP/1.0
------------------------------------------------------ */ process_rq( char *rq, int fd )
{
char cmd[BUFSIZ], arg[BUFSIZ]; /* create a new process and return if not the child */
if ( fork() != )
return; strcpy(arg, "./"); /* precede args with ./ */
if ( sscanf(rq, "%s%s", cmd, arg+) != )
return; if ( strcmp(cmd,"GET") != )
cannot_do(fd);
else if ( not_exist( arg ) )
do_404(arg, fd );
else if ( isadir( arg ) )
do_ls( arg, fd );
else if ( ends_in_cgi( arg ) )
do_exec( arg, fd );
else
do_cat( arg, fd );
}
上面只列出了一部分HTTP Server代码,下面列出处理CGI请求的代码:
do_exec( char *prog, int fd )
{
FILE *fp ; fp = fdopen(fd,"w");
header(fp, NULL);
fflush(fp);
dup2(fd, );
dup2(fd, );
close(fd);
execl(prog,prog,NULL);
perror(prog);
}
这里用到了Unix I/O重定向技术,也就是把脚本里面的标准输出(Java的System.out.print()Python 的 print )直接连接到fd,也就是说你在脚本里面的 print 结果就是用户接受到的结果。
当然CGI这种技术是最基本的也是效率最低的,每次一个CGI请求都要fork()一次,而且你只能在CGI脚本要和Web Server在同一台机器上。现在出现很多技术取代它比如FastCGI,SCGI,而每个不同的语言都对CGI进行了扩展,形成了自己的规范比如Python的WSGI。而且每种语言在将Web系统部署在HTTP Server上面上的时候都有自己的解决方案,最常用的就是扩展HTTP Server的模块,编写相应的处理模块,比如Python的 mod_python其实本质上就是在Apache中嵌入一个Python解释器。而现在非常流行的一种架构方案就是 HTTP服务器做前端代理,接受用户请求,对于静态文件请求则直接响应给用户,对于动态请求则转发给运用服务器,运用服务器将处理的结果反馈给HTTP服务器,然后HTTP服务器在返回给用户。也就是 Server/Gateway 模式,比如 Python中组合 Nginx+Gunicorn,Nginx是代理服务器,而Gnicorn 是 WSGI服务器,Nginx将动态请求转发给Gnicorn,Gnicorn在将请求按照封装为符合WSGI规范的的请求,然后在调用相应的的app,由于WSGI服务器由Python编写,所以可以直接调用对应的方法即可,不用在fork()。(这里的处理过程类似于CGI但是规范之Python独有的WSGI规范,更加适合Python处理)详情处理过程可参看Python的 wsgiref模块。 WSGI类似于Java的Servlet,Gnicron类似于Tomcat.
如果想了解CGI的处理过程,建议直接看 Python CGI的源码,非常容易理解。
如果想了解 CGI/1.1 规范 请点击 http://tools.ietf.org/html/draft-robinson-www-interface-00
粗谈CGI的更多相关文章
- sql 中的Bulk和C# 中的SqlBulkCopy批量插入数据 ( 回顾 and 粗谈 )
通常,我们会对于一个文本文件数据导入到数据库中,不多说,上代码. 首先,表结构如下. 其次,在我当前D盘中有个文本文件名为2.txt的文件. 在数据库中,可以这样通过一句代码插入. Bulk in ...
- 粗谈Android中的对齐
在谈这个之前先啰嗦几个概念. 基线:书写英语单词时为了规范书写会设有四条线,从上至下第三条就是基线.基线对齐主要是为了两个控件中显示的英文单词的基线对齐,如下所示: Start:在看API的时候经常会 ...
- python粗谈面向对象(二)
浅谈super() super并不是一个函数,是一个类名,形如super(B, self)事实上调用了super类的初始化函数,产生了一个super对象:Python的多继承类是通过mro的方式来保证 ...
- 粗谈CDN
CDN:内容分发网络(content delivery network) 1.DNS解析到最快(有可能是地理上最近,也有可能是地理上远但是链路最好)的CDN缓存设备 2.从CDN获取已经缓存的资源 3 ...
- 粗谈MySQL事务的特性和隔离级别
网上对于此类的文章已经十分饱和了,那还写的原因很简单--作为自己的理解笔记. 前言 此篇文章作为自己学习MySQL的一些个人理解,使用的引擎是InnoDb.首先先讲讲事务的概念,在<高性能 ...
- 粗谈pcap_next_ex()
pcap_next_ex(pcap_t* p,struct pcap_pkthdr** pkt_header,const u_char** pkt_data) 功能: 从interface或离线记 ...
- 粗谈shell脚本风格
注意:此风格并非官方版本,为个人在编写和维护脚本程序时总结出来的民间版本.0. 开头:除去开头的#!/bin/bash,最前面的就是脚本描述注释了,视个人喜好而定,例如: ############## ...
- python粗谈面向对象(一)
1.面向过程编程vs函数式编程 面向过程编程 以计算对象的元素个数为例. str_1 = 'abcdefg' count = 0 for i in str_1: # 统计字符串元素个数 count + ...
- netstat和net命令粗谈
网络连接查看命令netstat netstat -a 查看开启了哪些端口,常用netstat -an netstat -n 查看端口的网络连接情况,常用netstat -an netstat -v 查 ...
随机推荐
- 通过mybatis读取数据库数据并提供rest接口访问
1 mysql 创建数据库脚本 -- phpMyAdmin SQL Dump -- version 4.2.11 -- http://www.phpmyadmin.net -- -- Host: lo ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(40)-精准在线人数统计实现-【过滤器+Cache】
原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(40)-精准在线人数统计实现-[过滤器+Cache] 系列目录 上次的探讨没有任何结果,我浏览了大量的文章 ...
- 打开WEBBROWSER的选中文件路径
uses CommCtrl, httpApp, ShellApi; { GetWebPath(WebBrowser1); } function GetWebPath(web: TWebBrows ...
- 经典SQL语句大全之提升
二.提升 1.说明:复制表(只复制结构,源表名:a 新表名:b) (Access可用)法一:select * into b from a where 1<>1(仅用于SQlServer)法 ...
- MongoDB 安装与启动
一.MongoDB简单介绍 MongoDB是一个高性能,开源.无模式的文档型数据库.是当前NoSql数据库中比較热门的一种.它在很多场景下可用于替代传统的关系型数据库或键/值存储方式. Mongo使用 ...
- 基于HTML5的SLG游戏开发(一):搭建开发环境(1)
开发环境: 1.操作系统:MacOS 10.8.5 2.本地web服务器:Apache 2.2.24 (Window环境推荐使用WampServer) 3.编码工具:WebStrom 7.0 4.调试 ...
- 【转载】ASP.NET支持多语言
ASP.NET 2.0中实现:1.使用工具自动生成本地化资源(LocalResources) 首先建立一个WEB工程,如图所示:双击Default.aspx,切换到[设计]视图,从工具箱里拖一个But ...
- post get
/// <summary> /// Get方法 /// </summary> /// <param name="serverUrl">url地址 ...
- (转)javaScript call 函数的用法说明
call 方法 请参阅 应用于:Function 对象 要求 版本 5.5 调用一个对象的一个方法,以另一个对象替换当前对象. call([thisObj[,arg1[, arg2[, [,.argN ...
- memcached和mongodb 在windows下安装
要在新机器上安装memcached和mongodb服务,折腾了一天,终于把这两个服务在windows下跑起来了. memcached主要参考http://www.rootop.org/pages/27 ...