Принципы построения счётчиков попаданий на страницу с использованием интерфейса CGI.
![]()
Для включения в страницу счётчика попаданий обычно используется изображение числа попаданий, записанное в графическом файлае. Этот файл формируется на стороне Web сервера CGI-модулем и передаётся по протоколу HTTP Web браузеру, который отображает число в виде изображения на HTML-странице. При этом текст HTML страницы выглядит следующим образом:
<IMG SRC="URI_CGI_модуля">, где URI_CGI_модуля задаёт адрес CGI-модуля, выполняющего формирование изображения.
Текущее значение счётчика может храниться в файле в том же каталоге, где и CGI-модуль, формирующий изображение. При каждом вызове CGI-модуля из файла читается текущее значение счётчика и выполняется формирование изображения. После этого значение счётчика увеличивается на 1 и снова записывается в файл.
Формирование изображения в общем случае осуществляется чтением графических файлов с изображением цифр, и запись прочитанных данных в стандартный выходной поток. Файлы с изображением цифр хранятся на Web сервере вместе с CGI модулем.
Существует несколько подходов к передачи изображения браузеру:
1. Каждый разряд числа попаданий на страницу соответствует отдельному изображению на HTML-странице. При этом может использоваться один CGI-модуль, формирующий тот или иной разряд в соответствии с задаваемыми ему параметрами. Текст HTML-страницы в таком случае может выглядеть следующим образом:
<IMG
SRC="URI_CGI_модуля?digit=0">
<IMG
SRC="URI_CGI_модуля?digit=1">
<IMG
SRC="URI_CGI_модуля?digit=2">
<IMG
SRC="URI_CGI_модуля?digit=3">
<IMG
SRC="URI_CGI_модуля?digit=4">
Пример приведён для пятиразрядного счётчика. Увеличение значения счётчика может выполняться после формирования последнего разряда. Недостаток такого метода заключается в следующем. Так как браузер не всегда загружает изображения в том порядке, в каком они располагаются на странице, может возникнуть такая ситуация, когда сначала будет загружено последнее изображение (после чего выполняется увеличение значения счётчика), а затем будут загружены остальные изображения. Но так как значение счётчика увеличилось не после формирования всех изображений, оставшиеся изображения цифр могут не соответствовать реальному значению счётчика. Достоинство данного метода заключается в том, что изображения цифр могут храниться в формате GIF, при этом их размер будет меньше, чем у аналогичных изображений в формате BMP, также имеется возможность задавать прозрачный цвет.
2. Для вывода значения счётчика требуется только одно изображение на HTML-странице, при этом вызов CGI-модуля, выполняющего формирование изображения будет выполняться только один раз и соответствовать примеру, приведённому в начале статьи. Для описания недостатков данного метода рассмотрим алгоритм формирования изображения. Очевидно, что конечное изображение должно формироваться из изображений цифр, записанных в файле в каком-либо графическом формате. Но изображение в стандартный выходной должно записываться по строкам. Поэтому сначала нужно прочитать первую строку изображения, соответствующего последнему разряду числа, затем первую строку изображения, соответствующего предпоследнему разряду, и т.д. Затем читаются вторые строки и т.д. Так как изображение в формате GIF хранится в упакованном виде, то нет возможности прочитать определённую строку изображения. Вывод: изображение должно храниться в графическом файле в виде битовой карты или растра. Графическим форматом, соответствующим этому описанию, является формат BMP. В этом случае изображение хранится в файле в неупакованном виде и можно читать его по строкам. Но тогда объём информации, передаваемый в стандартный выходной поток будет значительно больше, чем если бы изображение передавалось в формате GIF. К недостаткам можно также отнести невозможность задавать прозрачный цвет. Достоинством данного метода является отсутствие ошибок при выводе значения счётчика, а также то, что для вывода значения счётчика требуется только одно изображение на HTML-странице. Первый недостаток (больший объём данных по сравнению с GIF) устраняется путём уменьшения размера изображения в разумных пределах. Так, например, изображение цифры "1" в формате GIF занимает 1395 байт, а аналогичное изображение в формате BMP занимает 2550 байт (размер изображения 32 на 46 пикселей). Таким образом, незначительное сокращение размера изображения в формате BMP позволяет привести размер BMP файла к размеру файла в формате GIF с аналогичным изображением. Второй недостаток (невозможность задавать прозрачный цвет) может быть устранён путём оформления изображений цифр таким образом, чтобы они не нуждалось в прозрачном цвете (например, можно изобразить цифру в виде разряда кассового аппарата).
Особенности формирования изображений CGI-модулем на примере языков C и C++.
В языке C++ для записи данных в стандартный выходной поток используется предопределённое имя стандартного выходного потока cout и операция записи в поток. Такой метод позволяет работать с выходным потоком потоком, как с текстовым. Это означает, что при записи в поток символа конца строки ('\n', 0x0A), он автоматически заменяется парой символов 0x0D, 0x0A. То же самое можно сказать о функции printf в языке C. Однако данный метод записи в поток является неприемлемым, так как изображение должно записываться в виде двоичных, а не текстовых данных ("байт в байт"). Поэтому при использовании cout и printf браузер будет получать неверные данные и не сможет отобразить изображение. Для выхода из этой ситуации может использоваться функция _write стандартного заголовочного файла io.h. Данный заголовочный файл содержит операции для работы с файлами на низком уровне, что предполагает использование в функциях ввода-вывода так называемых файловых дескрипторов. Файловый дескриптор представляет собой целое 16 битное число, по которому операционная система может однозначно определить открытый файл. При этом существуют три зарезервированных значения дескрипторов: 0, 1, 2, 3 и 4. 0 соответствует стандартному устройству ввода, 1 - стандартному устройству вывода, 2 - стандартному устройству для вывода ошибок (обычно совпадает со стандартным устройством вывода), 3 - внешнему устройству (например, последовательный порт), 4 - стандартному печатающему устройству (обычно это параллельный порт). Функция _write имеет следующий прототип:
int _write(int handle, void *buf, unsigned len)
параметр handle
определяет файловый дескриптор;
параметр buf
определяет адрес буфера передачи
данных;
параметр len
определяет длину передаваемых
данных.
Передача изображения в стандартный выходной поток может выглядеть следующим образом:
#include
<io.h>
#define
IMG_SIZE ...
. . .
. . .
. . .
char
buf[IMG_SIZE];
_write(1,
buf, sizeof(buf));
. . .
. . .
Перед записью изображения в стандартный выходной поток в качестве заголовка ответа на HTTP-ответа передаётся строка с указанием MIME типа изображения, например: "Content-type: image/bmp\n\n".
Пример CGI модуля, выполняющего формирование изображения для 5 разрядного счётчика попаданий на страницу.
#include <fstream.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <io.h>
#define W 32
#define H 46
#define H_SIZE 54
#define PAL_SIZE 1024
unsigned numbers[5];
char images[10][W * H];
char pal[PAL_SIZE];
char header[H_SIZE];
char buf[W * H * 5 + H_SIZE + PAL_SIZE];
void LoadHeader()
{
ifstream in("0.bmp", ios::binary);
in.read(header, H_SIZE);
in.close();
}
void LoadPalette()
{
ifstream in("0.bmp", ios::binary);
in.seekg(H_SIZE, ios::beg);
in.read(pal, PAL_SIZE);
in.close();
}
void LoadImages()
{
char name[] = "0.bmp";
ifstream in;
for (int i = 0; i < 10; i++)
{
name[0] = char(i + '0');
in.open(name, ios::binary);
in.seekg(H_SIZE + PAL_SIZE, ios::beg);
in.read(images[i], W * H);
in.close();
}
}
void WriteImages()
{
header[0x12] = 0xA0;
_write(1, (void *)header, H_SIZE);
_write(1, (void *)pal, PAL_SIZE);
for (int j = 0; j < H; j++)
for (int i = 0; i < 5; i++)
_write(1, (void *)(images[numbers[i]] + j * W), W);
}
int main(void)
{
unsigned number, d = 1;
LoadHeader();
LoadPalette();
LoadImages();
ifstream in("number.txt");
in >> number;
in.close();
cout << "Content-type: image/bmp\n\n";
cout.flush();
for (int i = 0; i < 5; i++)
{
numbers[4 - i] = char((number / d) % 10);
d *= 10;
}
WriteImages();
number++;
ofstream out("number.txt");
out << number;
out.close();
}
Текст HTML страницы с применением счётчика попаданий.
<html>
<body text="#000080">
<center>
<h1>Web counter</h1>
<hr>
<img src="http://localhost/cgi-bin/counter.exe">
<hr>
</center>
</body>
</html>
![]()
Copyright © 1999 by HackMaster