Принципы построения счётчиков попаданий на страницу с использованием интерфейса 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