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

Hosted by uCoz