[返回]
摘自计算机世界日报
交通部信息研究所 郭 瑜
1 .前言
当用浏览器访问网上站点的主页时,我们会发现很多网页上都有各种各样的漂亮用户访问次数计数器。它一方面可以记录用户对自己网页的访问次数,以便进行各种统计分析;另一方面,随着网页上计数器中数字增加到很大时,无形中会加深用户的印象,逐渐地也会提高网页的知名度以吸引更多的用户来访问。
这里我们介绍一种利用C 和java Applet 生成美观的用户访问次数计数器的方法。
2 .基本思想
利用当前流行的Client /Server 结构,在网页上嵌入java Applet, 当用户用浏览器访问到该页面时,就启动Client 端的java Applet, 由java Applet 向位于Server 端的统计程序发出请求。Server 端的统计程序将原有的用户访问数加1并重新记录下来,然后将新的结果传送给Client 端的java Applet,再由java Applet 把新的结果用图像方式在浏览器中显示出来。
3 .后台的统计程序
后台的统计程序可以用很多语言编写,如Perl,C,C++ 等, 这里可以具体地根据server 端的平台而定。这里我们选用C,在Server 端的Solaris 平台上用gcc 进行编译。
后台的这个统计程序实际上很简单,但是,其中有一个比较重要的问题需要考虑,那就是“读写锁”的问题。因为同时可能会有多个用户来访问我们的网页,这样就会有多个进程同时要对记录用户访问次数的文件进行读写操作,因此,我们必须用“读写锁”来保证资源的互斥与共享。
C 中有一个函数fcntl(int file_handle,int lock_style) 就是用来进行文件“锁”操作的。
这里我们只要用fcntl(fp,F_RDLCK) 和fcntl(fp,F_WRLCK) 就够了。
函数fcntl(fp,F_RDLCK) 的作用是对句柄是fp 的文件设置“读”锁,这样就保证同时可有多个进程对该文件进行“读”,但同时要对它进行“写”的进程必须等待,直至“读”锁被打开。
函数fcntl(fp,F_WRLCK) 的作用是对句柄是fp 的文件设置“写”锁,这样就保证在某一时刻只能有一个进程对该文件进行“写”,其它要对该文件进行“写”或“读”的进程在此时都必须等待,直至“写”锁被打开。
在我们的程序中,利用函数fcntl(fp,F_RDLCK) 和fcntl(fp,F_WRLCK) 就能有效保证对文件的互斥与共享。
4 .前台的图形效果的生成
前台的显示程序用java 来编写。
4.1 读取后台统计程序输出到标准输出上的新的用户访问次数
这里我们生成类URL 的一个实例url(String address) , 其中的address 为后台统计程序的url 地址,比如“http://www.iicc.ac.cn/cgi-bin/count”(count 就是后台统计程序的名字)。然后我们再调用实例url 的成员方法url.openStream(), 该方法返回一个从该URL 链接中读取数据的输入流,也就是说返回从我们的后台统计程序那里读取数据的输入流,接着我们再生成类DataInputStream 的一个实例instream,它从刚才我们生成的那个输入流中读取数据。具体的程序段如下:
URL url = new url
("http://www.iicc.ac.cn/cgi-bin/count");
DataInputStream instream =
new DataInputStrea( url.openStream);
String buffer = instream.readLine();
Serialno = buffer.toCharArray();
这样字符数组Serialno 中存入的数据就是后台统计程序count 输出到标准输出上的新的用户访问次数。
4.2 用图像方式显示新的用户数
我们用一个含有10 个元素的Image 数组,存放绘有数字0 -9 的10 个图像文件, 然后根据得到的新的用户访问次数,把相应的一副副的图像给贴到Applet 区域上去。相应的程序段如下:
public void paint(Graphics g)
{
int j=0;
for(int i=0;i< 8;i++)
{
g.drawImage(imgs[serialno[i]-'0'],j,0,null);
j=j+imgs[serialno[i]-'0'].getWidth(this);
}
}
这样, 用户就可根据自己的喜好,用图像处理软件得到表示数字0 -9 的10 个图像文件, 并把它放到后台统计程序所在的目录下,就能通过浏览器看到自己设计的用户访问计数器了。
5 .说明
count.c 在Solaris 2.5.1 上用gcc 2.7.2.3 编译,count_visitor.java Wiondows95 上用Visual J++ 1.1 生成count_visitor.class。
下面就是前后台的完整的程序
后台的统计程序:
#include "stdio.h"
#include "sys/file.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "fcntl.h"
#include "sys/uio.h"
#include "unistd.h"
#include "stdlib.h"
main(){
int fp,i;
unsigned long long number;
char buffer[30],buffer1[30];
fp=open("counter.txt",O_RDONLY);
fcntl(fp,F_RDLCK);
read(fp,buffer,30);
fcntl(fp,F_UNLCK);
close(fp);
number=(unsigned long long)atol(buffer);
number++;
strcpy(buffer,ulltostr(number, buffer1));
for(i=0;i< 8-strlen(buffer);i++)
buffer1[i]='0';
buffer1[i]=0;
strcat(buffer1,buffer);
fp=open("counter.txt",O_WRONLY);
fcntl(fp,F_WRLCK);
write(fp,buffer,30);
fcntl(fp,F_UNLCK);
close(fp);
printf("Content-type: text/html\n\n");
printf(buffer1);
}
前台的显示程序:
//**************************************
****************************************
// count_visitor.java: Applet
//
//**************************************
****************************************
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
//=======================================
=======================================
// Main Class for applet count_visitor
//
//========================================
======================================
public class count_visitor extends Applet
{
String buffer = new String();
char serialno[];
Image imgs[] = new Image[10];
URL url;
public void init()
{
try
{
URL url = new URL("http://202.96.42.1/cgi-bin/count");
try
{
DataInputStream instream =
new DataInputStream(url.openStream());
buffer = instream.readLine();
serialno=buffer.toCharArray();
}
catch(IOException e) {}
}
catch(MalformedURLException e) {}
serialno=buffer.toCharArray();
imgs[0]=getImage(getDocumentBase(),"zero.gif");
imgs[1]=getImage(getDocumentBase(),"one.gif");
imgs[2]=getImage(getDocumentBase(),"two.gif");
imgs[3]=getImage(getDocumentBase(),"three.gif");
imgs[4]=getImage(getDocumentBase(),"four.gif");
imgs[5]=getImage(getDocumentBase(),"five.gif");
imgs[6]=getImage(getDocumentBase(),"six.gif");
imgs[7]=getImage(getDocumentBase(),"seven.gif");
imgs[8]=getImage(getDocumentBase(),"eight.gif");
imgs[9]=getImage(getDocumentBase(),"nine.gif");
}
public void paint(Graphics g)
{
int j=0;
for(int i=0;i< 8;i++)
{
g.drawImage(imgs[serialno[i]-'0'],j,0,null);
j=j+imgs[serialno[i]-'0'].getWidth(this);
}
}
}