Выбрать главу

 "<html>\n"

 " <head>\n"

 "  <meta http-equiv=\"refresh\" content=\"5\">\n"

 " </head>\n"

 " <body>\n"

 "  <p>The current time is %s </p>\n"

 " </body>\n"

 "</html>\n";

void module_generate(int fd) {

 struct timeval tv;

 struct tm* ptm;

 char time_string[40];

 FILE* fp;

 /* Определение времени суток и заполнение структуры типа tm. */

 gettimeofday(&tv, NULL);

 ptm = localtime(&tv.tv_sec);

 /* Получение строкового представления времени с точностью

    до секунды. */

 strftime(time_string, sizeof(time_string), "%H:%M:%S", ptm);

 /* Создание файлового потока, соответствующего дескриптору

    клиентского сокета. */

 fp = fdopen(fd, "w");

 assert(fp != NULL);

 /* Запись HTML-страницы. */

 fprintf(fp, page_template, time_string);

 /* Очистка буфера потока */

 fflush(fp);

}

Для удобства в этом модуле используются стандартные библиотечные функции ввода-вывода. Функция fdopen() возвращает указатель потока (FILE*), соответствующий дескриптору клиентского сокета (подробнее об этом рассказывается в приложении Б, "Низкоуровневый ввод-вывод"). Для отправки страницы клиенту вызывается обычная функция fprintf(), а функция fflush() предотвращает потерю данных в случае закрытия сокета.

HTML-страница, возвращаемая модулем time.so, содержит в заголовке тэг <meta>, который служит клиенту указанием перезагружать страницу каждые 5 секунд. Благодаря этому клиент всегда будет знать точное время.

11.3.2. Отображение версии Linux

Модуль issue.so (исходный текст приведен в листинге 11.7) выводит информацию о дистрибутиве Linux, с которым работает сервер. Традиционно эта информация хранится в файле /etc/issue. Модель посылает клиенту Web-страницу с содержимым файла, заключенным в тэге <pre></pre>.

Листинг 11.7. (issue.c) Серверный модуль, отображающий информацию о дистрибутиве Linux

#include <fcntl.h>

#include <string.h>

#include <sys/sendfile.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <unistd.h>

#include "server.h"

/* HTML-код начала генерируемой страницы. */

static char* page_start =

 "<html>\n"

 " <body>\n"

 "  <pre>\n";

/* HTML-код конца генерируемой страницы. */

static char* page_end =

 "  </pre>\n"

 " </body>\n"

 "</html>\n";

/* HTML-код страницы, сообщающей о том, что

   при открытии файла /etc/issue произошла ошибка. */

static char* error_page =

 "<html>\n"

 " <body>\n"

 "  <p>Error: Could not open /etc/issue.</p>\n"

 " </body>\n"

 "</html>\n";

/* Сообщение об ошибке. */

static char* error_message =

 "Error reading /etc/issue.";

void module_generate(int fd) {

 int input_fd;

 struct stat file_info;

 int rval;

 /* Открытие файла /etc/issue */

 input_fd = open("/etc/issue", O_RDONLY);

 if (input_fd == -1)

  system_error("open");

 /* Получение информации о файле. */

 rval = fstat(input_fd, &file_info);

 if (rval == -1)

  /* не удалось открыть файл или прочитать данные из него. */

  write(fd, error_page, strlen(error_page));

 else {

  int rval;

  off_t offset = 0;

  /* Запись начала страницы */

  write(fd, page_start, strlen(page_start));

  /* Копирование данных из файла /etc/issue

     в клиентский сокет. */

  rval = sendfile(fd, input_fd, &offset, file_info.st_size);

  if (rval == -1)

   /* При отправке файла /etc/issue произошла ошибка.

      Выводим соответствующее сообщение. */

   write(fd, error_message, strlen(error_message));

  /* Конец страницы. */

  write(fd, page_end, strlen(page_end));

 }

 close(input_fd);

}

Сначала модуль пытается открыть файл /etc/issue. Если это не удалось, клиенту возвращается сообщение об ошибке. В противном случае посылается начальный код HTML-страницы, содержащийся в переменной page_start, затем — содержимое файла /etc/issue (это делается с помощью функции sendfile(), о которой рассказывалось в разделе 8.12. "Функция sendfile(): быстрая передача данных") и, наконец конечный код HTML-страницы, содержащийся в переменной page_end.