C++后台实践:古老的CGI与Web开发

本文写给C/C++程序猿,也适合其他对历史感兴趣的程序猿

=============================================

谈到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。

在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/post


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技术来进行更多的处理。

  1. JS可以使用Ajax技术来向后台CGI发起数据请求。Ajax完成的是不需要刷新整个页面就可以加载后端数据(比如从数据库中取出)。
  2. CGI一般不再用于直接返回html页面,同时将复杂的计算、IO任务下沉到后端(后端可以进一步进行路由转发,实现负载均衡)。使CGI作为前后端之间的中间层。彼时CGI的职能是完成基本的数据交换:解析前端数据请求,再转发给对应后端;然后从后端取回数据,给前端返回XML或JSON。
  3. 前端JS利用XML/JSON中的数据来进行填充,绘制出丰富的页面。
时间: 2024-07-31 14:34:48

C++后台实践:古老的CGI与Web开发的相关文章

【Python】[08]基于CGI的Web开发

开始web开发前,先来认识几个概念: CGI(Common Gateway Interface):称为通用网关接口,是一个Internet标准. 官方解释是生成动态内容的过程,这个动态过程大概是:web服务器先找到所要执行的程序,然后执行找到的程序,再捕获程序的输出作为web响应,把它发回还在等待的web浏览器.这个程序被叫做CGI脚本. MVC(Model-View-Controller):模型-视图-控制器模式,这是设计web应用应遵循的模式,他有助于把代码分解成易于管理的功能模块或者组件.

【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OSS(https://www.aliyun.com/product/oss) 4.HTTPS(http://baike.baidu.com/view/14121.htm) 阅读目录结构 引: 一.准备工作 二.整体功能结构 三.具体实现步骤 四.关键点和问题处理 五.延伸与扩展 六.总结与思考 引:

《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(下)

目录 前言 第8章 用户认证 第9章 用户角色 第10章 用户资料 第11章 博客文章 第12章 关注者 第13章 用户评论 第14章 应用编程接口   前言 第1章-第7章学习实践记录请参见:<Flask Web开发——基于Python的Web应用开发实践>一字一句上机实践(上) 本文记录自己学习<Flask Web开发——基于Python的Web应用开发实践>的第8章-第14章内容.相比于刚开始学习第1-7章内容来说,本部分内容实战性更强,而且在书本上遇到的问题也相对较少,如果

Redis的Python实践,以及四中常用应用场景详解——学习董伟明老师的《Python Web开发实践》

首先,简单介绍:Redis是一个基于内存的键值对存储系统,常用作数据库.缓存和消息代理. 支持:字符串,字典,列表,集合,有序集合,位图(bitmaps),地理位置,HyperLogLog等多种数据结构. 支持事务.分片.主从复之.支持RDB(内存数据保存的文件)和AOF(类似于MySQL的binlog)两种持久化方式.3.0加入订阅分发.Lua脚本.集群等特性. 命令参考:http://doc.redisfans.com 中文官网:http://www.redis.net.cn 安装(都大同小

Web开发中前台与后台技术小结--关于EL表达式,JSTL,eval()函数

在我们日常Web开发中,常常用到EL表达式,JSTL标签,还有可能用到JavaScript中的eval().下面就这样技术和函数小小说明一下. EL(Expression Language),全称表达式语言,既然是开发语言,那么就像Java等语言一样是用来写语句的,通常是用在JSP页面中,我们为了获取JSP提供的内置对象的属性值, 我们通常会使用到EL表达式,比如开发中常见的一个需求就是获取项目的根目录,如果项目的名值变化了,我们在指定路径时,如果把项目的根目录用一个变量来表示的话,那么就不 需

Google Web开发最佳实践(一)

这篇文章最初是在阿里通信前端团队的github博客(http://aliqin.github.io)上看到的,原文地址https://developers.google.com/web/fundamentals/(要翻墙).既然要去阿里了,就得先熟悉熟悉环境,既然是最佳实践,就得自己亲自实践一下. 1.创建网站的内容和结构 内容是任何网站最重要的部分.所以让我们为内容而设计,而不要让设计支配内容.在这个手册中,我们首先确定我们需要的内容,基于这个内容创建一个页面结构,然后在简单的线性布局里呈现页

Mobile Web开发实践

移动设备的快速发展给用户带来了很大的便利.用户使用Android.iPhone和其它移动设备很容易接入互联网.移动设备对网页的性能要求比较高,下面就说说Mobile Web开发的一些心得. Viewport 当你用iPhone访问一个没有面向移动设备优化过的网站时(最好选取960px宽度的网站),你会发现iPhone上面刚好可以把整个页面显示出来,但是页面被缩小了非常多,字体非常小.这其实是Sarari默认把自己当成980px宽度来处理的,而所有iPhone(竖屏)的真实宽度为320px,事实上

移动Web开发实践

移动设备的高速发展给用户带来了非常大的便利.用户使用Android.iPhone和其他移动设备非常easy接入互联网. 移动设备对网页的性能要求比較高.以下就说说Mobile Web开发的一些心得. Viewport 当你用iPhone訪问一个没有面向移动设备优化过的站点时(最好选取960px宽度的站点).你会发现iPhone上面刚好能够把整个页面显示出来,可是页面被缩小了许多.字体很小.这其实是Sarari默认把自己当成980px宽度来处理的,而全部iPhone(竖屏)的真实宽度为320px,

WEB开发中前后台树形菜单的展示设计

在WEB开发中经常需要进行树形菜单的展示,本例通过不同角度的总结了如下三种实现方式: 通过JS的递归实现前端菜单DOM的动态创建 通过JSP的include指令结合JSTL表达式语言递归实现菜单的展示 通过扩展JSP的标签在后端实现菜单的DOM节点并响应给前端展示 针对第一种方法,可以采用JS的相关组件,或者使用JS的递归调用将服务端相应的数据组装成DOM节点内容,动态添加到菜单的Container中,网上的例子较多,在此不再赘述,本例就后两种方案进行讲解. 通过JSP的include指令结合J