NonSoft

アクセスカウンタCGIのサンプル(perl)


 サンプル(ソース/コード)
#!/usr/local/bin/perl
#---------------------------------------------------------------------
# ファイル名:NONcount.cgi:のんカウンター Ver1.04
#
# 更新履歴:
# 2002.01.01 Ver1.00 初版。              作成者(のん)
# 2002.01.02 Ver1.01 コメント変更           変更者(のん)
# 2002.03.08 Ver1.02 コメント変更           変更者(のん)
# 2002.03.21 Ver1.03 クロスサイトスクリプティング対応 変更者(のん)
# 2002.03.25 Ver1.04 クロスサイトスクリプティング対応 変更者(のん)
#
# Copyright (c) 2001 のん
# 著作権  :著作権は放棄しません。
#       ファイル名の変更は禁止します。
#       使用・改造・再配布は制限しませんが、
#       改造したものを配布する場合は更新履歴をいれてください。
#
# 開発目標 :各ページ個別のアクセス数を管理するために作成しました。
#
# 機能概要 :概略としては以下の機能があります。
#       SIDという単位でアクセス数を管理しますので、
#       複数のページのアクセス数を別々に表示出来ます。
#       同一IPアドレスからのアクセスは、カウントしません。
#       (更新ボタンによるカウントアップはしません。)
#
# 起動パラメータ:sid----SID(default="etc")
#         ino---カウンタイメージファイル番号(default="0")
#         len---カウンタ桁数(default="3")
# (注意)カウンタイメージファイル番号は、デフォルトで0〜7を用意して
#     います。数字素材は、digit_arcadia様の素材を使用しています。
#     digit_arcadia様のURLは
#     http://www.bea.hi-ho.ne.jp/show-boo/digit_arcadia.shtmlです。
#
# 起動例:
#  <img src=
#   "http://xxx.xxx.xxx/NONcount/NONcount.cgi?sid=non1&ino=5&len=6">
#
# 管理ファイル :(1)SIDファイル(data/sid_NONcount.txt)
#            SIDを1行単位で複数記述します。
#            各行に改行が必要です。
#         (2)カウンタファイル(data/xxx_NONcount.txt)
#            xxxの部分は、SIDです。
#            各SIDのカウンタの値を管理します。
#            1行目が、最終アクセスIPアドレスです。
#            2行目が、現在のカウンタの値です。
#
# 関連プログラム:GIFファイル連結ライブラリ Ver1.52(gifcat.pl)
#         GIFファイル連結ライブラリは、
#         http://wakusei.cplaza.ne.jp/twn/様のフリーウェアを
#         を使用しています。
#
# インストール方法:NONcount.cgiと関連プログラムをCGIが動作する
#          ディレクトリに置きます。(サーバーがUNIXの場合は、
#          ".cgi"ファイルと".pl"ファイルに実行権を与えます。)
#          NONcount.cgiと同じディレクトリにdataディレクトリを
#          作成し管理ファイルを置きます。
#          NONcount.cgiと同じディレクトリにimg?ディレクトリを
#          作成し"0.gif"から"9.gif"ファイルを置きます。
#          img?の"?"はイメージファイル番号です。
#
#---------------------------------------------------------------------
#---------------------------------------------------------------------
# 入力情報を取得
if($ENV{'REQUEST_METHOD'} eq "POST") {
	read(STDIN, $str, $ENV{'CONTENT_LENGTH'});
	$str = $str . "&" . $ENV{'QUERY_STRING'};
}elsif($ENV{'REQUEST_METHOD'} eq "GET") {
	$str = $ENV{'QUERY_STRING'};
}
$ip = $ENV{REMOTE_ADDR};

# ボタンをクリックした時の入力情報を個別に定義
$fs = "	";
if( $str ne "" ){
	%cgi = "";
	@part = split( '&' , $str );
	foreach $i (@part) {
		($variable,$value) = split('=',$i);
		$value =~ tr/+/ /;
		$value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C",hex($1))/eg;
#		&jcode'convert(*value, 'euc');
		$value =~ s/&/&amp;/g;
		$value =~ s/</&lt;/g;
		$value =~ s/>/&gt;/g;
		$value =~ s/"/&quot;/g;
		$value =~ s/'/&#39;/g;
		$value =~ s/\015\012/\012/g;
		$value =~ s/\015/\012/g;
		if( $cgi{$variable} eq "" ){
			$cgi{$variable} = $value;
		}else{
			$cgi{$variable} = $cgi{$variable} . $fs . $value;
		}
	}
	# 起動情報の個別定義
	$sid = $cgi{'sid'};
	$ino = $cgi{'ino'};
	$len = $cgi{'len'};
}

# デフォルト値の定義
if( $sid eq "" ){
	$sid = "etc";
}
if( $ino eq "" ){
	$ino = 0;
}
if( $len eq "" ){
	$len = 3;
}

# ファイル名をここに定義
$imgf = "img$ino";
$count_lock = "data/${sid}_NONcount.loc";
$count_file = "data/${sid}_NONcount.txt";
$sid_file = "data/sid_NONcount.txt";

# 同時アクセスを防止するためのロック
#sub sigexit { rmdir($count_lock); exit; }
$SIG{'PIPE'} = $SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = "sigexit";
for( $ded=0 ; $ded < 60 && 
	mkdir("$count_lock",0777)==0 ; $ded++ ){
	sleep(1);
}

# システムIDチェック
$sid_flg = 0;
open( SID , "$sid_file" );
while( <SID> ){
	chop($_);
	if( $sid eq $_ ){
		$sid_flg = 1;
		last;
	}
}
close( SID );
if( $sid_flg != 0 ){
	# カウントアップ
	$i=0;
	open(IN, "< $count_file");
	while(<IN>){
		chop($_);
		if( $i == 0 ){
			$i++;
			$beforeIp = $_;
		}else{
			$count = $_;
			last;
		}
	}
	close(IN);
	if( $beforeIp ne $ip ){
		open(OUT, "> $count_file");
		$count++;
		print(OUT "$ip\n");
		print(OUT "$count\n");
		close(OUT);
	}
	close(OUT);

	# CGIスクリプトの結果としてカウンターを書き出す
	$cntstr = sprintf(sprintf("%%0%dld", $len), $count);

	printf("Content-type: image/gif\n\n");
	for ($i = 0; $i < length($cntstr); $i++){
		$n = substr($cntstr, $i, 1);
		push(@files, "$imgf/$n.gif");
	}
	require "gifcat.pl";
	binmode(STDOUT);
	print &gifcat'gifcat(@files);
}
# ロックファイルを削除する
rmdir("$count_lock");
rmdir("$count_lock");
exit;

このサイトのアクセスカウンタ(のんカウンタCGI)の表示が遅い事について調査してみた。まず、ニフティ
ーが提供しているアクセスカウンタCGIと交換してみる。すると早い。良く見ると、ニフティーのアクセスカ
ウンタCGIはテキストカウンタだった。のんカウンタは数字のGIFイメージを連結してカウンタ表示を行な
っているので、それが遅いのかもしれないと思い、のんカウンタCGIにテキストカウンタの機能を追加してみ
る。と言ってもどうすれば良いのか分からない。。。とりあえず、ニフティーのアクセスカウンタCGIのUR
LにIEから直接アクセスしてみる。するとIEの画面が真っ白になり、ソースに以下のようなJavaScr
iptが表示された。
<!--
document.write("000991")
//-->
どうやら、カウンタ値をJavaScriptに埋め込んで送り返せば良いようなので、のんカウンタCGIに
も実装してみる。カウンタの原理は一緒なので実装はすぐに完了した。その効果を試してみると、確かに表示処
理は速くなった。しかしページ全体が表示されるまでが遅くなった。一瞬IEが真っ白になるこの現象はGIF
イメージを使用していた時の遅さより気になる。。。GIFイメージの時はページ全体の表示は速かった(アク
セスカウンタの表示だけが遅かった)。調べてみると、JavaScriptをページの先頭や途中で実行する
と、そのJavaScriptが完了するまで次の文字等が表示されない仕様のようだ。ニフティーのアクセス
カウンタCGIはとりあえずページの最後に配置してあったため今まで気付かなかった。では、JavaScr
iptをページの最後に実行しアクセスカウンタをページの途中に置く方法は無いだろうか?早速試してみた。
方法はアクセスカウンタを表示したい場所に<span id="_NONcount"></span>を設定して置き、ページの最後でJ
avaScriptを実行する方法。CGIが返すJavaScriptは以下のようなも。
<!--
var _span = document.getElementById("_NONcount");
_span.innerHTML = "000991";
//-->
この方法でページ全体の表示は今まで同様に速くなったが、アクセスカウンタはページの最後に表示されるため
、GIFイメージの時とあまり変わらないようだった。ここまで調べて、結局、のんカウンタCGIの処理速度
を早くするしか解決する方法が無いことに気付いた。では、のんカウンタCGIは何が遅いのか。のんカウンタ
CGIは複数のページのアクセス数を管理している。そのため各ページにIDを設定し毎回それをチェックして
いる。これが遅さの原因の一つと考えられるので、その処理を見直し、と言うより削除した。ん?、少し早くな
った?また、同時にアクセスされた時にカウンタの値がおかしくならないように排他制御を行なっているが、こ
の処理も遅さの原因と考えられるので削除した。変更前とくらべると随分早くなった気がする。これなら耐えら
れる。しかし、削除した2つの処理の対策をしなければならない。各ページのIDチェックの代わりは、各ペー
ジのカウンタファイルがオープン出来るかどうかでチェックするようにした。カウンタファイルは変更前もオー
プンしていたため、処理を増やさずIDチェックの代わりが出来た。排他処理は同時アクセス時、片方が書き込
み、片方が読み込みの時に読込値がおかしくなり、おかしな値をカウントアップして書き込む事を防ぐための処
理。つまり、読み込んだカウンタ値がおかしい時(0以下の時をおかしいと判断)はカウンタ表示を88888888と
し、カウンタファイルへの書き込みは行なわないようにした。つまり、同時アクセス時はカウンタ表示を諦める
と言う事。。。同時アクセスが多発するサイトでもないので、とりあえずこれで様子を見る事にした。
(のんカウンター Ver1.07 このサイト専用のため未公開)