本文写给C/C++程序猿,也适合其余对历史感兴趣的程序猿html
=============================================前端
谈到web开发,你们首先想到的PHP、JavaEE/JSP、.NET/ASP、Ruby on rails、Python的Django等等。可谓百花齐放,你通常不会想到C++和Web开发有什么关系,但其实动态网页的开发(web开发)但是在这些动态网页语言诞生以前就存在了的。因此C/C++也是能够作web开发的,它利用的技术是——CGI。程序员
在天地初开,混沌未分之时,动态网页语言还没有出世,要实现动态网站依赖的就是CGI。谷歌/百度一下CGI,可能会出现不少名词:CGI脚本、CGI程序、CGI标准等等。其实这些都是站在不一样角度来讲的,CGI即Common Gateway Interface的缩写,直译为“通用网关接口”。第一次听这个名字,我也不知道是个什么鬼东西。归根结底 CGI就是一个接口协议。协议就是你们公认的一套标准(叫CGI标准也能够),好比网络协议。你们都遵照一套标准,就减小了沟通的难度。进行CGI开发,就是编写一个CGI可执行程序。其实各类语言均可以编写CGI,不但Java、Python、PHP、C#……能够,并且Shell也能够。固然C和C++也能够。因为早期CGI不少是由Perl(脚本语言)开发的,因此CGI程序也称CGI脚本,其实这个称呼不必定准确。由于C++编译出的可执行文件一样能够是CGI。web
在PHP和Java大行其道的今天,不少人看来用C++编写CGI是几乎淘汰的技术了(其实这到否则,只是比较小众罢了)。因此若是你对C/C++感兴趣或者对历史感兴趣均可以阅读本文。数据库
一次网页请求与响应
在进行网页浏览时,一般就是经过一个URL请求一个网页,而后服务器返回这个网页文件给浏览器。浏览器在本地解析该文件渲染成咱们看到的网页。然而一般咱们看到的网页不是静态网页,也就是说在服务端是没有这个网页文件,它是在网页请求的时候动态生成的,好比PHP/JSP网页。依据你请求的参数不一样,所返回的内容不一样。
同理,若是是请求一个CGI程序的时候(
好比在浏览器直接输入CGI程序的URL,或者提交表单的时候发送给CGI程序),CGI程序负责解析从前端传递过来的参数,理解它的意图而后返回数据,好比返回HTML、XML或JSON等。
WARNNING:Apache默认没有打开CGI的支持,须要进行CGI的配置。具体方法能够自行百度。
预备前端知识
假设你是一个C++程序员,你可能对前端不熟(OK,我也不熟),在接下来的讲述以前,你要先掌握一些预备的前端知识(尽可能少讲前端),你不须要知道如何渲染出一个美轮美奂的网页,但你须要知道前、后端如何交互。前端页面如何发送数据,一个普通的HTML页面一般的作法,你只需知道以下几种:
- form表单提交(html原生)
- js操纵下的表单提交
- js经过Ajax请求数据
这里知讲第一种(最简单的):
<h1>表单提交</h1>
<form action="/cgi-bin/hello.cgi" method="get">
<table>
<tr>
<td>用户名:</td>
<td><input name="username"/></td>
</tr>
<tr>
<td>密码:</td>
<td><input name="password"/></td>
</tr>
<tr>
<td><input type="submit" value="OK"/></td>
</tr>
</table>
</form>
form标签的action属性的值表示的就是表单要提交到url,即表单提交之后要跳转的页面(Ajax能够达到无跳转拉取数据,刷新页面),这里action属性值的是cgi程序的url地址。(WARNNING:/ 对应的是网站根目录,而不是Linux文件系统根目录哦)。method属性表示数据请求方式,有两种:get和post。不赘述。
我输入用户名jellywang,密码123456以后,点击OK按钮,即向
当前域名/cgi-bin/hello.cgi 的程序序提交了表单,而且携带参数username=jellywang。而后页面会跳转到这个cgi(就像普通网页跳转,浏览器地址栏更新同样)。
若是是get请求。那么浏览器地址栏的URL看起来像这样:localhost:/cgi-bin/hello.cgi?username=jelly&password=123456。很显然这是一种不够安全的方式,因此咱们还可使用post请求。这样地址栏就看不到这种提交的参数了。(其实post也不够安全,不鼓励直接提交明文密码的方式,本文仅做示例,安全登陆不上本文重点)
环境变量与CGI处理
当前端页面经过get或post方法向cgi程序提交了数据之后,那么接下来cgi程序该如何解析呢?答案是
环境变量。不管是Linux系统或Windows系统都有环境变量的概念。Linux用户在配置不少环境的时候,都不得不在系统配置文件中和环境变量打交道。CGI程序便是经过从环境变量中取值来得到参数的。这里介绍几个环境变量(更多的请自行百度):
REQUEST_METHOD编程 |
前端页面数据请求方式:get/postjson |
QUERY_STRING后端 |
采用GET时所传输的信息浏览器 |
CONTENT_LENGTH安全 |
STDIO中的有效信息长度 |
SCRIPT_NAME |
所调用的CGI程序的名字 |
SERVER_NAME |
服务器的IP或名字 |
SERVER_PORT |
主机的端口号 |
这些环境变量是从何而来,是谁定义的?是Linux吗?POSIX吗?固然不是。这里就要再次声明一下CGI是一个接口协议,这些环境变量就是属于该协议的内容,因此不论你的server所在的操做系统是Linux仍是Windows,也不论你的server是Apache仍是Nginx,这些变量的名称和含义都是同样的。实际就是Apache/Nginx在将这些内容填充到环境变量中,而具体填充规范则来自于CGI接口协议。
在C语言标准中有获取环境变量值得库函数——getenv。(头文件stdlib.h)
//好比
chr* str = NULL;
str = getenv("QUERY_STRING");
对于get请求,能够从环境变量QUERY_STRING中取出字符串 username=jelly&password=123456。而后程序本身作字符串的解析操做,解析出参数的key和value。而对于post请求,则是直接经过标注输入(STDIN)来获取这个参数字符串,好比使用scanf或cin均可以。
在解析了请求、进行了相应的逻辑处理以后(好比检查用户名密码是否一致),CGI程序要向前端页面返回内容,这是经过标准输出(STDOUT)完成的,好比printf或cout,你能够返回xml,json,plain text或一个html网页等等。这一步完成的是就是HTTP的响应过程。因此在返回直接的数据以前,要先输出HTTP协议的首部。好比,假设你想返回一个html网页,那么你首先要输出:
cout<<"Content-Type:text/html\n\n"<<endl;
WARNNING:这里要注意,必定要输出两个换行符(\n)。由于HTTP协议的首部和消息实体(如HTML代码)
之间用空行分割。
后面直接cout出html代码(好比输出你刚才输入的用户名成功登录)。前端页面就会收到这些html代码,而后浏览器就渲染成网页啦。这就是一次CGI完成的动态网页操做了。
Cgicc库
进行C++的CGI编程,须要手动进行字符串的解析处理,还有自行管理首部。好比资源转移了,要返回302,而且在首部用Location给出新地址。很显然,这些东西对于PHP、Python等语言都有内置的解决方案。对于C++就须要第三方库了。这里推荐一个GNU的开源库——
Cgicc。能够知足经常使用的各种需求,除了解析get/post请求外,还能重定向,还能够设置Cookie,还能够上传文件等等等等。
美中不足的就是Cgicc库不支持SESSION。可是这个问题不大,咱们能够很容易使用Cookie来实现SESSION功能。因为
CGI自己是请求一次就建立一个进程,返回以后进程就结束(下文的FastCGI除外)。这时要在服务端维持一个SESSION的变量可选的解决方案是:用文件存储或者在Redis、Memcached等内存数据库中存储。而发给客户端的SESSIONID就用Cgicc已经支持的Cookie功能来完成,就能够了。
CGI的痛点与FastCGI
CGI是一种标准,并不限定语言。因此Java、PHP、Python均可以经过这种方式来生成动态网页。可是实际上这些动态语言却不多这样用。原来是CGI有一大硬伤。那就是每次CGI请求,那么Apache都有启动一个进程去执行这个CGI程序,即颇具Unix特点的fork-and-execute。当用户请求量大的时候,这个fork-and-execute的操做会严重拖慢Server的进程。而Java的Servlet技术则是一种常驻内存的技术,不会频繁的发生进程上下文的建立和销毁操做。
时势造英雄,FastCGI技术应运而生。简单来讲,其本质就是一个常驻内存的进程池技术,由调度器负责将传递过来的CGI请求发送给处理CGI的handler进程来处理。在一个请求处理完成以后,该处理进程不销毁,继续等待下一个请求的到来。FCGI技术一出,CGI又必定程度上焕发了第二春。PHP-FPM自己是使PHP支持FCGI技术的一个Patch,如今已经被归入PHP标准。固然,支持C++的FCGI技术也出现了,Apache有FCGI的模块能够安装,好比mod_fcgid。
现代CGI的编程范式
前面咱们知道,CGI能够直接返回一个html网页。CGI程序自己也能够进行各类计算、逻辑处理任务。随着各种web先后端技术的发展,以及大数据、高并发的Server使用场景愈来愈多。现代的CGI的用法,在发生变化。
如今,愈来愈多的任务从后端转移到前端,前端页面利用丰富的Js技术来进行更多的处理。
- JS可使用Ajax技术来向后台CGI发起数据请求。Ajax完成的是不须要刷新整个页面就能够加载后端数据(好比从数据库中取出)。
- CGI通常再也不用于直接返回html页面,同时将复杂的计算、IO任务下沉到后端(后端能够进一步进行路由转发,实现负载均衡)。使CGI做为先后端之间的中间层。彼时CGI的职能是完成基本的数据交换:解析前端数据请求,再转发给对应后端;而后从后端取回数据,给前端返回XML或JSON。
- 前端JS利用XML/JSON中的数据来进行填充,绘制出丰富的页面。