另类的网站计数器——CGI编程的简例及其相关编程规范

最近事情比较多,也没有怎么更新文章。正好昨天突然想到要给这个小站的底部放一个访问计数器,就是“您是本站第XXX位访客”这样的。用PHP实现当然很简单,但是今天我不用PHP,而用C语言来编写这个网站计数器的后端。通过这个很简单的例子,我们可以体会到CGI编程的作用和相关规范。

首先来叙述一下CGI编程的规范。PHP, ASP等程序都可以看成是CGI程序,而CGI程序并不一定要以这些脚本语言编写。用其他任何计算机语言,比如C, C++, Delphi等都是可以的。客户端提交数据给服务端CGI程序进行处理,CGI程序处理后返回给客户端浏览器,这就是完整的CGI工作流程。客户端GET, POST等方式提交的表单等数据,是作为参数一并提交给CGI程序的,因此,以C语言为例,我们以

1
int main(int argc, char * argv[])

的方式编写CGI程序就可以接收到提交的参数并进行处理。CGI程序给浏览器的输出是服务器程序将标准输出(stdout)重定向后实现的,因此我们只要向stdout按照一定规范输出数据,该数据将可以立即显示在客户端浏览器上。

下面我们来进行这个C语言网站计数器的编写。整个计数器分为前端和后端两个部分,后端为C语言编写的CGI程序,前端可以使用Ajax或者PHP的Curl等功能进行CGI程序的调用。服务器的环境是CentOS 5 32bit/Apache, 其他服务器比如Nginx应当类似。CGI程序需放置在cgi-bin目录中,我这里是/var/www/cgi-bin/, 若找不到的话可以在终端(或SSH)中执行

1
find / -name cgi-bin

命令来查找。另外,编译C源程序需要安装gcc, CentOS下可以执行

1
yum install gcc

来安装,Debian下执行

1
apt-get install gcc

安装。

计数器的工作流程是这样的:前端页面被访问,调用CGI程序,CGI程序读取之前的访问量并将其加一后返回给前端页面。为了方便、简洁与高效,我们使用磁盘文件而不是MySQL来存储这样一个访问量数据。另外,我们可以实现分别统计总访问量和当天访问量的功能,只需在磁盘中分别储存这两个数据,每天24:00时让系统自动执行一个初始化的脚本将当天访问量的数据清零即可。网站计数器的CGI部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include
int main(void)
{
    FILE * fp;
    long counter[2];
    printf("Content-type: text/html\n\n");    //注
    if ((fp=fopen("/var/counter.dat","rb+")) == NULL){
        puts("Failed in opening file.");
        exit(1);
    }
    fread(counter,sizeof(*counter),2,fp);
    printf("You are No.%ld visitor today, and No.%ld visitor in all.",++*counter,*(counter+1)+*counter+1);
    rewind(fp);
    fwrite(counter,sizeof(*counter),1,fp);
    fclose(fp);
    return 0;
}

需要注意的是我标注的那一行,这是CGI编程规范的一部分,就是CGI程序给浏览器的输出的第一行必须是标题,比如这里的Content-type: text/html,且标题后必须要有一个单独的空行。只有输出了这样的标题后CGI程序才能继续向浏览器输出,否则访问时会产生500错误。

上面这个程序读入的数据的位置是/var/counter.dat, 因此在运行之前需要保证这个文件已经建立,并且权限足够(需要将其用户组属设为apache服务器的用户)。程序中sizeof(long)一般为4,不过可能由于编译环境不同而有差异。另外,为了速度,程序读文件采用的是二进制流,因此/var/counter.dat可能需要用程序来建立,或者使用DD命令亦可,长度为8个字节。

将上面这个程序编译连接后命名为counter.cgi, 然后复制到cgi-bin目录下,这时我们在浏览器中访问http://your_ip/cgi-bin/counter.cgi应该能看到一句话的正确的输出,若访问遇到500错误,请自行检查Apache错误日志进行错误排查。

下面我们再编写一个程序用于每天24点自动将当天访问量清零。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include
#include
int main(void)
{
    FILE * fp;
    long counter[2];
    if ((fp=fopen("/var/counter.dat","rb+")) == NULL){
        puts("Failed in opening file.");
        exit(1);
    }
    fread(counter,sizeof(*counter),2,fp);
    *(counter+1)=*counter+*(counter+1);
    *counter=0;
    rewind(fp);
    fwrite(counter,sizeof(*counter),2,fp);
    fclose(fp);
    printf("\nInitialized.\n");
    return 0;
}

编译后设置计划任务即可。这样,后端我们就编写完毕了。需要指出的是,我上面第一个程序已经将返回到浏览器的输出形式写好了,如果不喜欢这样的形式则可以自行更改,也可以输出一串json格式的数据由前端Javascript或者PHP进行解析,这都是可以的。

前端部分可以用Javascript的XMLHttpRequest对象,即类似Ajax的方式实现,也可以用PHP的file_get_contents()等函数获取CGI程序的输出,这里以Javascript为例简要说明CGI程序的调用。假设已新建GetCounter为XMLHttpRequest的实例,则:

1
2
3
4
5
6
7
8
9
10
11
12
13
function RequestNumber() {
    var CGI_url="http://your_ip/cgi-bin/counter.cgi";
    GetCounter.onreadystatechange=showNumber;
    GetCounter.open("GET",CGI_url,true);
    GetCounter.send(null);
}
function showNumber() {
    if (GetCounter.readyState == 4) {
        if (GetCounter.status == 200) {
            counter.innerHTML = GetCounter.responseText;
        }
    }
}

我们在页面底部新建一个<div id=”counter”></div>的标签,则使用Javascript调用RequestNumber()函数即可在网页底部看到计数器的输出了。至此,这个用C语言编写的另类计数器全部编写完毕。

可以看出,很多类似的工作,比如数据库查询、用户登录处理等内容都可以使用其他语言编写的CGI程序来完成,其优势在于安全性高、无数据库连接信息泄露危险、运算及执行速度快、字符与文件处理能力强等。以后我将写一些CGI编程其他方面,如环境变量、标题设置,以及文件上传、表单提交等功能的实现等内容。转载请注明转自香菇肥牛的博客http://qing.su

5 Responses

  1. 无纯洁说道:

    不错,学习了。。。

  2. tennfy说道:

    学到了,CGI 脚本是任何运行在web服务器上的程序. CGI意思是Common Gateway Interface,上面的c,c++,c#等等都是放在/var/www/cgi-bin/里么

    • 香菇肥牛说道:

      可以放在这个里面,也可以为服务器上的每个网站单独设置文件夹

  3. Youth.霖说道:

    哇!!
    1看到了这个代码高亮插件也不错
    2涨姿势了原来C语言也可以用于网页
    3了解到了cgi-bin的意思,以前我只知道网页不存在有时会打开cgi-bin/default.cgi
    4为什么不在底部放个演示呢

    • 香菇肥牛说道:

      这个破插件没法缩进。。。。。= =
      嗯嗯,好建议~谢啦~

tennfy进行回复 取消回复

电子邮件地址不会被公开。 必填项已用*标注