"<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>.
#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.