本文写给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中的数据来进行填充,绘制出丰富的页面。

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

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

    目录 前言 第1章 安装 第2章 程序的基本结构 第3章 模板 第4章 Web表单 第5章 数据库 第6章 电子邮件 第7章 大型程序的结构   前言 学习Python也有一个半月时间了,学到现在感觉 ...

  2. 写给自己的web开发资源

    web开发给我的感觉就是乱七八糟,而且要学习感觉总是会有东西要学习,很乱很杂我也没空搞,(其实学习这个的方法就是去用它,什么你直接用?学过js么学过jquery么?哈哈,我没有系统的看完过,但是也做出 ...

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

    提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...

  4. Redis在WEB开发中的应用与实践

    Redis在WEB开发中的应用与实践 一.Redis概述: Redis是一个功能强大.性能高效的开源数据结构服务器,Redis最典型的应用是NoSQL.但事实上Redis除了作为NoSQL数据库使用之 ...

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

    首先,简单介绍:Redis是一个基于内存的键值对存储系统,常用作数据库.缓存和消息代理. 支持:字符串,字典,列表,集合,有序集合,位图(bitmaps),地理位置,HyperLogLog等多种数据结 ...

  6. python实现基于CGI的Web应用

    python实现基于CGI的Web应用 本文用一个“网上书店”的web应用示例,简要介绍如何用Python实现基于CGI标准的Web应用,介绍python的cgi模块.cigtb模块对编写CGI脚本提 ...

  7. 8102 年的现代 Web 开发最佳实践(笑)

    简评:8102 年了,现在 web 开发的最佳实践是什么,让本文来告诉你.原文只提到一部分,可以查看 reddit 上对此文的评论查看补充的最佳实践 https://old.reddit.com/r/ ...

  8. Web前端开发最佳实践(1):前端开发概述

    引言 我从07年开始进入博客园,从最开始阅读别人的文章到自己开始尝试表达一些自己对技术的看法.可以说,博客园是我参与技术讨论的一个主要的平台.在这其间,随着接触技术的广度和深度的增加,也写了一些得到了 ...

  9. Servlet学习笔记【1】--- 背景和基础知识(CGI、Web服务器发展史、Servlet简介、任务、继承结构)

    本文主要讲Servlet的基础知识和背景知识. 1 CGI简介 CGI(Common Gateway Interface 公共网关接口)是WWW技术中最重要的技术之一,有着不可替代的重要地位.CGI是 ...

随机推荐

  1. spark2.1:使用df.select(when(a===b,1).otherwise(0))替换(case when a==b then 1 else 0 end)

    最近工作中把一些sql.sh脚本执行hive的语句升级为spark2.1版本,其中遇到将case when 替换为scala操作df的方式实现的问题: 代码数据: scala> import o ...

  2. SpringMVC(一):搭建一个SpringMVC helloword项目

    操作步骤: 1)下载spring framework开发包,给eclipse安装spring开发插件,如何安装开发插件&下载开发包请参考我的博文:<Spring(一):eclipse上安 ...

  3. python自带的web服务器

    python自带的web服务器 python自带的包可以建立简单的web服务器 BaseHTTPServer 提供基本的web服务和处理类 SimpleHTTPServer 包含执行get请求的Sim ...

  4. Hadoop学习笔记(七):初识spark

    1. spark的安装: a). 首先复制一台虚拟机出来(复制任意一台master和slave即可),然后将其ip修改为192.168.XX.200,并将其hostname更改为c(hostnamec ...

  5. iOS 私有API调用

    最近自己在做一个小程序,想实现一个一键设置手机壁纸的功能.但在iOS公开的API里找不到相关的方法,只能从私有API入手. 网上有不少教程,不过都不是很详细.从google和https://stack ...

  6. 关于阮大神的es6标准入门第一章

    题记:之前在10月份的时候写过阮大神的es6的第一章,但是由于那段时间项目组的动荡,所以也没有什么后续,导致我现在对es6基本都忘的差不多了,不过,现在换了新公司,最近也没什么任务,所以现在开始重新写 ...

  7. [LeetCode] Map Sum Pairs 映射配对之和

    Implement a MapSum class with insert, and sum methods. For the method insert, you'll be given a pair ...

  8. MySQL · 引擎特性 · InnoDB 同步机制

    前言 现代操作系统以及硬件基本都支持并发程序,而在并发程序设计中,各个进程或者线程需要对公共变量的访问加以制约,此外,不同的进程或者线程需要协同工作以完成特征的任务,这就需要一套完善的同步机制,在Li ...

  9. Efficient&Elegant:Java程序员入门Cpp

    最近项目急需C++ 的知识结构,虽说我有过快速学习很多新语言的经验,但对于C++ 老特工我还需保持敬畏(内容太多),本文会从一个Java程序员的角度,制定高效学习路线快速入门C++ . Java是为了 ...

  10. Java IO(五)==>>重点

    打印流:PrintStream与PrintWriter PrintStream 该类的定义如下: public class PrintStream extends FilterOutputStream ...