本篇是系列文章中的第二篇,讲述大名鼎鼎的CGI技术。CGI 全称为Common Gateway Interface (通用网关接口),目的是能够让服务器能够方便的调用外部程序。CGI本身是一套协议和规范,原则上只要是拥有读写文件功能的编程语言都可以用来编写CGI程序,例如C,C++,Perl,Visual Basic,Shell等等,历史上用来编写CGI程序使用最广泛的是Perl语言,连PHP一开始也是用Perl编写的,估计也受这个传统的影响。服务器在认为这是一个CGI请求时,会调用相关CGI程序,并通过环境变量和标准输出将数据传送给CGI程序,CGI程序处理完数据,生成html,然后再通过标准输出将内容返回给服务器,服务器再将内容交给用户,CGI进程退出,在这个过程中,服务器的标准输出对应了CGI程序的标准输入,CGI程序的标准输出对应着服务器的标准输入,相当于利用两条管道建立了进程间的通信。

在今天的网站中,偶尔还能看到CGI的身影,比如Apache的下载页: http://httpd.apache.org/download.cgi

下面是用C编写的一个CGI小程序,向服务器返回数据只需要将数据写入标准输出即可,可见CGI程序的编写也是相当容易的:

cgi.c :

#include <stdio.h>int main()
{char MimeType[]="text/html";fprintf(stdout, "Content-type: %s\r\n\r\n", MimeType);  //输出响应头,响应头之后要加两个"\r\n"fprintf(stdout, "<html><head><title>CGI小程序</title></head>\n");fprintf(stdout, "<body>由C编写的CGI小程序</body></html>\n");return 0;
}

由于Nginx不支持CGI(支持CGI的升级版FastCGI和SCGI),而Apache原生支持CGI,所以这里选用Apache来举例

首先要对Apache进行一定的配置,使之支持CGI程序,配置如下:

LoadModule cgi_module modules/mod_cgi.so  #注意这项配置是否已经存在,已存在就不要重复配置AddHandler cgi-script .cgi    #设置cgi程序的扩展名,这里.cgi扩展名文件会被当作CGI来执行#设置cgi-bin的目录权限,假设 /var/www/html 为你的DocumentRoot
<Directory "/var/www/html/cgi">AllowOverride NoneOptions Indexes ExecCGI  # ExecCGI 表示该目录允许执行CGI,如果没有加这个权限,即使是.cgi也没有权限执行Order allow,denyAllow from all
</Directory>

重启Apache,把上面的C编写的CGI小程序用gcc编译成可执行文件cgi.out,并放到你配置的CGI目录,这里为 /var/www/html/cgi-bin

[root@localhost c]# gcc cgi.c -o test.cgi
[root@localhost c]# mv test.cgi /var/www/html/cgi/

然后浏览器访问该文件 ,就可以看到输出结果了

从以上可以看出,cgi编程和普通的编程并没有太大的区别,cgi小程序也是可以直接运行的可执行文件,并且大多数编程语言都可以进行cgi编程,这或许也是cgi能够流行起来的原因之一,下面看用 php写一个CGI程序。

cgi.php :

#!/usr/bin/env php
<?php
// cgi.php
//由于php脚本不是可执行文件,这里用shell的方式来执行php脚本fwrite(STDOUT, "Content-type: text/html\r\n\r\n");
fwrite(STDOUT, "<html><head></head><body><b>PHP编写的CGI程序演示 ". date("Y-m-d H:i:s") ."</b></body></html>\n");

给cgi.php 添加可执行权限

[root@localhost cgi]# chmod +x cgi.php
[root@localhost cgi]# ./cgi.php
COntent-type: text/html<html><head></head><body><b>PHP编写的CGI程序演示 2017-06-23 15:41:00</b></body></html>

配置Apache支持.php扩展名的CGI小程序

AddHandler cgi-script .cgi .php    #设置cgi程序的扩展名,这里 .cgi 和 .php 扩展名文件会被当作CGI来执行

重启Apache,访问该php文件:

可以看到CGI协议非常简单,但当前为止还没有涉及获取http请求的GET和POST参数,这就要说前面提到过的环境变量和CGI小程序的标准输入了,

下面修改前面的C和PHP小程序,使之支持读取GET和POST参数

cgi.c :

//cgi.c
#include <stdio.h>
#include <stdlib.h>
extern char **environ;  //调用环境变量
int main()
{char MimeType[]="text/html";fprintf(stdout, "Content-type: %s\r\n\r\n", MimeType);  //输出响应头,响应头之后要加两个"\r\n"fprintf(stdout, "<html><head><title>CGI小程序</title></head><body>\n");char **env;//循环输出环境变量for(env = environ; *env != NULL; env++){fprintf(stdout, "%s<br>\n", *env);}char *query_string = getenv("QUERY_STRING"); //获取环境变量QUERY_STRING,也就是GET参数fprintf(stdout, "---------------<br>query_string:%s<br>-------------<br>", query_string);fprintf(stdout, "</body></html>\n");return 0;
}

再次编译之后浏览器访问:

http://127.0.0.1:8888/cgi/test.cgi?user=zyee&age=27

访问结果:

HTTP_HOST=127.0.0.1:8888
HTTP_CONNECTION=keep-alive
HTTP_CACHE_CONTROL=max-age=0
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
HTTP_UPGRADE_INSECURE_REQUESTS=1
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
HTTP_ACCEPT_ENCODING=gzip, deflate, sdch
HTTP_ACCEPT_LANGUAGE=zh-CN,zh;q=0.8,en;q=0.6
HTTP_COOKIE=UM_distinctid=15b130cb46b156-0f197c7d0f440a-3e64430f-1fa400-15b130cb46d3f2; CNZZDATA1258531050=1910116482-1490671422-%7C1490671422; _cnzz_CV1258531050=isLogin%7CLogout%7C1493265278109; CNZZDATA5201073=cnzz_eid%3D509423027-1490969081-http%253A%252F%252F127.0.0.1%253A801%252F%26ntime%3D1490969081; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1496995397
PATH=/sbin:/usr/sbin:/bin:/usr/bin
SERVER_SIGNATURE=
Apache/2.2.15 (CentOS) Server at 127.0.0.1 Port 8888SERVER_SOFTWARE=Apache/2.2.15 (CentOS)
SERVER_NAME=127.0.0.1
SERVER_ADDR=10.0.2.15
SERVER_PORT=8888
REMOTE_ADDR=10.0.2.2
DOCUMENT_ROOT=/var/www/html
SERVER_ADMIN=root@localhost
SCRIPT_FILENAME=/var/www/html/cgi/test.cgi
REMOTE_PORT=58373
GATEWAY_INTERFACE=CGI/1.1
SERVER_PROTOCOL=HTTP/1.1
REQUEST_METHOD=GET
QUERY_STRING=user=zyee&age=27
REQUEST_URI=/cgi/test.cgi?user=zyee&age=27
SCRIPT_NAME=/cgi/test.cgi
---------------
query_string:user=zyee&age=27
-------------

可以看到环境变量中包含了很多有用信息, 包括当前的URL,GET参数,客户端IP地址,请求头等等信息。

下面用PHP脚本来演示获取POST参数

cgi.php :

#!/usr/bin/env php
<?php
// cgi.php
//由于php脚本不是可执行文件,这里用shell脚本的方式来让php脚本可执行$post = fread(STDIN, 1024);  // post参数从标准输入读取fwrite(STDOUT, "COntent-type: text/html\r\n\r\n");
fwrite(STDOUT, "<html><head></head><body>");fwrite(STDOUT, "\npost: $post\n");fwrite(STDOUT, "<b>PHP编写的CGI程序演示 ". date("Y-m-d H:i:s") ."</b>");
fwrite(STDOUT, "</body></html>\n");
~                                          

curl模拟POST请求

[root@localhost cgi]# curl -d "name=zyee&age=27" "http://127.0.0.1/cgi/cgi.php"
<html><head></head><body>
post: name=zyee&age=27
<b>PHP编写的CGI程序演示 2017-06-23 17:25:26</b></body></html>

上传文件类型的POST请求  (  Multipart/form-data  ):

[root@localhost cgi]# curl -F "filename=@/var/www/html/cgi/test.cgi" "http://127.0.0.1/cgi/cgi.php"
<html><head></head><body>
post: ------------------------------1a7c7aacb288
Content-Disposition: form-data; name="filename"; filename="test.cgi"
Content-Type: application/octet-stream<b>PHP编写的CGI程序演示 2017-06-23 19:32:08</b></body></html>

CGI编写对于有编程基础的人来说是相当容易上手的,那为什么又会被历史所遗弃呢,这就不得不说到CGI的运行方式问题了,每当一个CGI请求过来时,服务器会fork一个子进程来执行相应的CGI程序,当请求结束时,该CGI进程也随之结束,这样不停fork进程的开销是非常大的,这是造成CGI程序效率低下的主要原因。我们可以让CGI程序睡眠一段时间来观察这个过程,比如修改以上程序如下:

#include <stdio.h>
#include <unistd.h>int main()
{sleep(10);  //睡眠10秒钟char MimeType[]="text/html";fprintf(stdout, "Content-type: %s\r\n\r\n", MimeType);  //输出文件类型fprintf(stdout, "<html><head><title>CGI小程序</title></head>\n");fprintf(stdout, "<body>由C编写的CGI小程序</body></html>");return 0;
}

然后请求该页面,观察服务器进程信息,可以看到httpd进程fork出的一个子进程在执行CGI程序,并且请求结束该子进程便会退出

正是这种缺陷,所以Apache之后又推出了CGI模块的升级版 mod_cgid 模块,我们来看看官网对该模块的介绍:

Except for the optimizations and the additional ScriptSock directive noted below, mod_cgid behaves similarly to mod_cgiSee the mod_cgi summary for additional details about Apache and CGI.

On certain unix operating systems, forking a process from a multi-threaded server is a very expensive operation because the new process will replicate all the threads of the parent process. In order to avoid incurring this expense on each CGI invocation, mod_cgid creates an external daemon that is responsible for forking child processes to run CGI scripts. The main server communicates with this daemon using a unix domain socket.

大致意思:

除了优化性能和增加了 ScriptSock指令外,mod_cgid 和 mod_cgi是非常类似的...

在当前的unix操作系统上,一个多线程的服务fork一个子进程代价是非常高昂的,因为fork出来的子进程会复制它父进程的所有线程。为了避免每次执行CGI程序都引起这个高代价的操作, mod_cgid模块创建一个外部的守护进程来负责创建子进程执行CGI程序,server和这个守护进程间通过unix domain socket来通信。

在后面我会向大家介绍一种更高级的技术,FastCGI 以及更高效的进程管理器 php-fpm 以及 Apache的 mod_fcgid模块,请大家关注后续文章。

That's it! :)

动态web技术(二) --- CGI相关推荐

  1. Web应用工作原理、动态网页技术

    我们知道应用程序有两种模式,C/S模式和B/S模式.C/S模式是客户端/服务器模式,这类 应用程序一般独立的运行.B/S模式是浏览器/服务器模型,需要借助浏览器来运行. web应用程序一般就是B/S模 ...

  2. 《JAVA Web技术及应用》读书笔记

    2019.06.11 第一章 JAVA Web 入门 1.1 Web应用概述 Web应用是一种通过互联网访问的应用程序,使用网页语言编写,通过浏览器运行的动静态网站.在实际应用中大多数网站采用动静结合 ...

  3. 静态网页和动态网页技术详解

    静态网页 一.静态网页的特点 1. 所谓静态是指的就是网站内容固定不变.  静态网页一经制成,内容就不会再变化, 如要修改网页的内容,就必须修改源码,重新上传到服务器. 2. 静态网页内容通常以HTM ...

  4. 计算机二级web题目(6)--动态网页技术概述

    1 [单选题] 下列不属于动态网页格式的是(D). A. ASP B. JSP C. ASPX D. VBS 2 [单选题]相对比较早出现的服务器端动态网页技术是(B). A. ASP B. CGI ...

  5. 用WEB技术栈开发NATIVE应用(二):WEEX 前端SDK原理详解

    摘要: WEEX依旧采取传统的web开发技术栈进行开发,同时app在终端的运行体验不输native app.其同时解决了开发效率.发版速度以及用户体验三个核心问题.那么WEEX是如何实现的?目前WEE ...

  6. 深入分析Java Web技术内幕读书笔记(二)浅析DNS域名解析过程

    上一篇文章<浅析Web请求过程>讲述的是如何发起HTTP请求,对于请求发起过程中很重要的一个步骤--DNS解析过程的描述是一带而过,本篇文章将跟着DNS解析过程来分析域名是如何解析的. 一 ...

  7. JSP..由sun公司提供的动态web资源开发技术

    JSP jsp介绍 由sun公司提供的动态web资源开发技术 Jsp是为了解决html只能输出页面,不能展示动态数据,而servlet适合开发动态web资源,不适合响应页面的问题 Jsp技能写html ...

  8. Java Web技术经验总结(二)

    该系列的第一篇在此:Java Web技术经验总结一,主要包含我在日常工作中的经验和心得体会(如有不足之处欢迎指出). Maven的使用经验 依赖的scope有test.provided.compile ...

  9. 深入分析Java Web技术内幕(二)

    DNS域名解析 第一步:浏览器检查缓存中有没有这个域名对应的解析过的IP地址,有则解析结束. 第二步:如果用户的浏览器缓存中没有,浏览器会查找操作系统缓存中是否有这个域名对应的DNS解析结果.host ...

最新文章

  1. 2021年大数据ELK(十八):Beats 简单介绍和FileBeat工作原理
  2. 限制HTTP数据包发送Referer
  3. tf.keras.losses.SquaredHinge 损失函数 示例
  4. 深入理解并使用python的模块与包
  5. 比赛,幸福度_幸福与生活满意度
  6. Java 蓝桥杯 算法 和为T
  7. C#编码简单性之泛型篇(如何编写简短的C#代码,随时更新)
  8. iOS 15 通知的新功能
  9. aix系统vi修改命令_aix系统VI编辑器的操作
  10. 《大师谈游戏设计——创意与节奏》【笔记二】
  11. 如何批量隔行删除Excel行
  12. java算法——通过身份证号获取出生的年月日
  13. 自动写稿机器人下载,写稿机器人有哪些,写稿机器人软件下载
  14. B端设计指南——表格 究竟应该如何设计?
  15. 微信公众号如何添加文档附件【教程】
  16. CSS3变形之2D变形
  17. ArcMap基础操作——去除影像背景值
  18. 随机森林算法(RandomForest)实现MNIST手写体数字识别
  19. 六、Django ORM关联模型
  20. 10000+门店的蜜雪冰城,帮你找回软件赚钱的初心

热门文章

  1. css div内容垂直居中
  2. matlab2019使用simulink对控制系统进行仿真
  3. leetcode2021年度刷题分类型总结(十)哈希表 (python)
  4. c# 控制台485串口连接
  5. 深度强化学习-A3C算法
  6. android 手机号码隐藏其中4位
  7. Maxscale实现MySQL读写分离
  8. 颠覆传统的X41T详细评测
  9. 详解软件测试中白盒测试基本概念及四种白盒测试方法以及六种逻辑覆盖法(语句覆盖、判定覆盖、条件覆盖、判定条件覆盖、条件组合覆盖、路径覆盖)
  10. 基于MATLAB中APP Designer的采样定理的可视化