Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Новости ИТ Программы Книги Games   Обои   Экспорт RSS E-Books
 
Поиск по сайту

TOP-10 программ
Symantec Norton Ghost 9.0
Partition Magic 8.0.2 Pro
Xilisoft 3GP Video Converter v3.1.7.0616b
Norton AntiVirus 2005
Xilisoft 3GP Video Converter v2.1.52.831b
Антивирус Касперского Personal 5.0.303 beta 2
RAR Password Cracker 4.12
ABBYY PDF Transformer v1.00.820
MP3 To Ringtone Gold v3.02
Mobtime Cell Phone Manager v5.3.1
 
Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
 
 

   Программирование -> Delphi / Pascal -> Веб-сервер своими руками


Веб-сервер своими руками

Как известно, все уже написано до нас. В том числе и веб-серверы на любой вкус. С другой стороны, наш собственный веб-сервер все равно будет обладать несомненным преимуществом - мы сможем контролировать его код и, по мере необходимости, добавлять новые возможности

Лирическое отступление

Когда мой сайт обитал на boom.ru, с отладкой все было в порядке: изменяешь в блокноте, переходишь в Оперу, жмешь F5 - и все изменения налицо. Но когда я начал переходить (и пока еще, правда, не перешел) на h10.ru с его возможностями PHP, Perl: - с отладкой начались проблемы. Как вам наверняка известно, PHP и Perl исполняются не на стороне браузера, а на стороне сервера. А поскольку Apache из диска "К + П" № 3 я так и не установил (мой CD-ROM приказал долго жить), была предпринята попытка написать собственный веб-сервер (естественно, на Delphi).

Эта статья предназначена для тех, кому, как и мне, не хочется тратить дорогие интернет-минуты на отладку своих проектов (тем более если в проектах этих используется PHP или другие CGI-средства).

С чего начать

Начнем мы, как всегда, с запуска нашего Delphi. После появления формы перебросим из палитры компонентов TIdHTTPServer (который, по большому счету, и будет выполнять за нас всю грязную работу по соединению с клиентом и общению с ним). Нам же останется лишь переадресовывать запросы клиента на соответствующие файлы. Кроме того, я порекомендовал бы также перетащить на форму еще и TButton. И в его реакции на нажатие написать код запуска нашего сервера:

procedure TForm1.Button1Click (Sender: TObject);
begin
Self.IdHTTPServer1.Active:=True;
end;
, а в событие Form1.OnDestroy ():
procedure TForm1.FormDestroy (Sender: TObject);
begin
Self.IdHTTPServer1.Active:=False;
end;

Теперь поподробнее рассмотрим событие idHTTPServer.OnCommandGet, которое имеет тип:

TIdHTTPGetEvent = procedure (AThread: TIdPeerThread; 
  RequestInfo: TIdHTTPRequestInfo; ResponseInfo:
    TIdHTTPResponseInfo) of object; 

Где:

  • AThread - поток, который содержит информацию о подключении;
  • параметр RequestInfo содержит информацию о запрашиваемых данных;
  • ResponseInfo используется для передачи результата выполнения запроса.

Для проверки работоспособности нашего сервера создадим файл (Response.txt) примерно следующего содержания:

<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>This is the test</h1>
</body>
</html>

А в обработчике события idHTTPServer1.OnCommandGet напишем такой код:

ResponseInfo.ContentStream:=TFileStream.Create 
('D:\Response.txt',fmOpenRead); 

Настройка браузера предельно проста - нужно всего лишь указать в настройках прокси-сервера название компьютера, на котором находится сервер, и соответствующий порт (я использую 800-й).

После запуска сервера открываем наш уже настроенный браузер и набираем любой адрес. В результате мы должны получить то, что было написано в файле Response.txt. Но это нам мало что дает - какой бы адрес мы ни использовали, результат будет одним и тем же. Для получения чего-то более похожего на веб-сервер нужна небольшая доработка:

if RequestInfo.Host='www.test.com' then
begin
ResponseInfo.ContentStream:=TFileStream.Create (
'D:\Projects\HTMLProjects\MySite\'
  +RequestInfo.Document,fmOpenRead);
end;

Что мы здесь делаем:

  • во-первых, проверяем, какой хост запрашивает клиент;
  • если это www.test.com, то переадресовываем запрос на соответствующий файл, содержащийся в каталоге с полной копией нашего сайта (D:\Projects\HTMLProjects\MySite);
  • посылаем результат клиенту.

Теперь, если в поле браузера ввести строку www.test.com/index.html, мы увидим начальную страницу (если она называется index.html) нашего сайта - со всеми ссылками, рисунками, скриптами и аплетами.

CGI

Все это, конечно, хорошо - но что мы с этого имеем? Практически ничего - ведь точно такой же результат мы бы получили, если бы просто набрали в браузере адрес: D:\Projects\HTMLProjects\MySite\Index.html. И, естественно, тот аргумент, что www.test.com/index.html набирать быстрее, устроит не всех (вернее, всех не устроит). К счастью, разрабатываем сервер мы сами - значит, можем внедрять в него все, что нам угодно: Standalone CGI, WinCGI, ISAPI (NSAPI), Apache CGI, PHP, Perl, Python, MySQL:

В этой главе мы остановимся именно на разработке поддержки Standalone CGI.

Итак. StandaloneCGI - программа, работающая под DOS или Windows (и не только, можно и под Linux, только для этого придется перекомпилировать наш сервер), которая при запуске выдает в устройство стандартного вывода требующуюся информацию. Все необходимые параметры передаются ей посредством переменных окружения.

Принцип работы сервера с такими программами таков:

  • задать необходимые переменные окружения;
  • запустить программу;
  • перенаправить результат из стандартного вывода на другой объект (например, в файл);
  • закрытие программы: Закрывается автоматически после вывода всей информации;
  • передать содержимое созданного файла клиенту;
  • удалить файл.

Для того чтобы не засорять память ненужными переменными окружения, воспользуемся функцией запуска приложений CreateProcess, которая перед запуском приложения создает для него частное адресное пространство со своими переменными окружения, которое освобождается после завершения процесса.

Прототип этой функции выглядит так:

function CreateProcess (lpApplicationName: PChar;
  lpCommandLine: PChar;
lpProcessAttributes, lpThreadAttributes: 
  PSecurityAttributes;
bInheritHandles: BOOL; dwCreationFlags: DWORD; 
  lpEnvironment: Pointer;
lpCurrentDirectory: PChar; const lpStartupInfo:
  TStartupInfo;
var lpProcessInformation: TProcessInformation):
   BOOL; stdcall;

Где:

  • LpApplicationName:PChar - название приложения (плюс полный путь к нему);
  • LpCommandLine:PChar - командная строка приложения (все параметры, которые передаются приложению через командную строку);
  • LpProcessAttributes, lpThreadAttributes - в нашем случае NIL (подробнее об этих параметрах читайте Win32 help);
  • BInheritHandles:Bool - в наше случае False;
  • DwCreationFlags - CREATE_NEW_PROCESS_GROUP или DETACHED_PROCESS;
  • LpEnvirounment:Pointer - указатель на строку, которая содержит переменные окружения, необходимые нашей программе;
  • LpCurrentDirectory:PChar - рабочий каталог нашей программы;
  • LpStartupInfo:TStartupInfo - параметры запуска приложения;
  • LpProcessInformation:TProcessInformation - переменная, в которую помещаются все дескрипторы запущенного приложения.

Результат: True - если приложение нормально напустилось, и False - в противном случае.

Для переадресации устройства вывода воспользуемся параметром lpStartupInfo, который имеет следующую структуру (см. таблицу 1).

Таблица 1. Параметры функции CreateProcess
Параметр Описание
cb: DWORD; Размер данной структуры
lpReserved: Pointer; Зарезервировано
lpDesktop: Pointer; Для NT - указатель на строку, которая содержит название дисплея, на который выводится информация приложения (здесь не используется)
lpTitle: Pointer; Для консольных приложений - строка, которая отображается на панели задач
dwX: DWORD; X-координата приложения (нам она не нужна - 0)
dwY: DWORD; Y-координата приложения (нам она не нужна - 0)
dwXSize: DWORD; Ширина окна приложения (нам она не нужна - 0)
dwYSize: DWORD; Высота окна приложения (нам она не нужна - 0)
dwXCountChars: DWORD; Для консольных приложений задает ширину в "текстовых единицах" (для нас - 0)
dwYCountChars: DWORD; Для консольных приложений задает высоту в "текстовых единицах" (для нас - 0)
dwFillAttribute: DWORD; Задает сочетание цвета фона и цвета символа (это не для нас - опять 0)
dwFlags: DWORD; См. таблицу 2
wShowWindow: Word; Одна из констант SW_ - режим отображения приложения (это не для нас)
cbReserved2: Word; Зарезервировано
lpReserved2: PByte; Зарезервировано
hStdInput: THandle; Дескриптор стандартного ввода
hStdOutput: THandle; Дескриптор стандартного вывода
HStdError: THandle; Дескриптор стандартного устройства вывода ошибки
Таблица 2. Описание флагов запускаемого процесса
Значение Описание
STARTF_USESHOWWINDOW Если не задан, wShowWindow игнорируется
STARTF_USEPOSITION Если не задан,, dwX, dwY игнорируются
STARTF_USESIZE Если не задан dwXSize, dwYSize игнорируются
STARTF_USECOUNTCHARS Если не задан, dwXCountChars, dwYCountChars игнорируются
STARTF_USEFILLATTRIBUTE Если не задан, dwFillAttribute игнорируется
STARTF_FORCEONFEEDBACK Очень много написано, все равно не использую
STARTF_FORCEOFFFEEDBACK Очень много написано, все равно не использую
STARTF_USESTDHANDLES Если не задан, hStdInput, hStdOutput, hStdError не используются

Как же все это будет выглядеть в программе? Для начала приведу функцию, которая возвращает в параметре Result:TStringList значения переменных окружения:

procedure CreateServerVariables 
 (RequestInfo:TIdHttpRequestInfo;var
Result:TStringList);
begin
if not Assigned (Result) 
 then Result:=TStringList.Create;
Result.Add ('HTTP_HOST='+RequestInfo.Host);
Result.Add 
 ('REQUEST_METHOD='+RequestInfo.Command);
Result.Add ('URL='+RequestInfo.Document);
Result.Add 
 ('QUERY_STRING='+RequestInfo.UnparsedParams);
Result.Add ('REMOTE_ADDR='+RequestInfo.RemoteIP);
Result.Add 
 ('HTTP_ACCEPT='+RequestInfo.Headers.Values ['Accept']);
Result.Add 
 ('HTTP_USER_AGENT='+RequestInfo.Headers.Values
['User-Agent']);
Result.Add ('SERVER_PROTOCOL='+sServerProtocol);
Result.Add ('SERVER_SOFTWARE='+sServerSoftware);
end;

Но просто передать значения Result в CreateProcess нельзя - для этого используем еще одну сервисную функцию:

function FormEnv (Data:TStringList):String;
var i:integer;
begin
Result:='';
if Data<>nil then
begin
For i:=0 to Data.Count-1 do
Result:=Result+Data [i]+#0;
Result:=Result+#0;
end;
end;

Нам осталось сделать переадресацию со стандартного устройства вывода в наш файл и запустить приложение:

function RunCGI 
 (Command:PChar;Data:TStrings):PChar;
var FS:TFileStream;
SI:TStartupInfo;
PI:TProcessInformation;
SL:TStringList;
Env:Pointer;
EnvStr:String;
begin
Result:=PChar (sNoErrorNoResult);
FS:=TFilestream.Create (ExtractFileDir 
 (ParamStr
(0))+'\temp.html',fmCreate);
try
FillChar (SI,SizeOf (SI),0);
SI.cb:=SizeOf (SI);
SI.dwFlags:=STARTF_USESTDHANDLES;
SI.hStdOutput:=FS.Handle;
SI.hStdInput:=GetStdHandle 
 (STD_INPUT_HANDLE);
SI.hStdError:=GetStdHandle 
 (STD_ERROR_HANDLE);
EnvStr:=FormEnv (Data);
if not CreateProcess
(Command,'',nil,nil,False,
 CREATE_NEW_PROCESS_GROUP
or DETACHED_PROCESS,Pointer 
 (EnvStr),PChar (ExtractFileDir
(ParamStr (0))),SI,PI) then
Result:=PChar (sCGIStartError) else
begin
if WaitForSingleObject 
 (PI.hThread,5000)=WAIT_FAILED then
begin
Result:=PChar (sTimeoutError);
exit;
end;
SL:=TStringList.Create;
try
FS.Position:=0;
SL.LoadFromStream (FS);
Result:=PChar (SL.Text);
finally
SL.Free;
end;
end;
finally
FS.Free;
if FileExists (ExtractFileDir 
 (ParamStr (0))+'\temp.html') then
DeleteFile (ExtractFileDir 
 (ParamStr (0))+'\temp.html');
end;
end;

Порядок работы:

  • сначала мы создаем файл (temp.html), в который будем переадресовывать информацию из приложения, и обнуляем переменную SI;
  • заполняем необходимые поля SI;
  • заполняем строку с переменными окружения;
  • запускаем наш CGI;
  • ждем конца выполнения (5 с);
  • передаем результат выполнения в SL, а тот, в свою очередь,- в переменную Result;
  • удаляем файл temp.html.

После выполнения этой функции возвращаемое значение передаем в ResponseInfo.ContentText.

А как же PHP, Perl:

Возможно, кто-то из читателей посетует: "обещал же о PHP, о Perl рассказать:". Рассказываю. PHP и Perl, как и другие подобные вещи, создаются не для одного IIS или PWS, а для использования со многими серверами и на многих платформах. Для примера: стандартная поставка PHP включает в себя ActiveScript (то есть ActiveX), стандартные ISAPI, NSAPI, Apache и Apache2, а также Standalone CGI. Имеется, кроме того, и библиотека для JavaServlets.

Значит, для того чтобы научить наш сервер работать с PHP, надо, во-первых, скачать пакет PHP, прописать необходимые пути к нему в переменной PATH. В код сервера добавить фильтрацию по расширению запрашиваемого документа (php, php3, php4) и передать эти файлы в качестве параметров (вспомните параметр lpCommandLine) на обработку CGI'шке php.exe. Результат, как и прежде, следует вернуть в ContentText.

Автор: Михаил Продан
Источни: кwww.comizdat.com

 
Популярные книги

Системное администрирование на 100 % (+CD)

Подробнее

Дизайн помещений и интерьеров в 3ds max 7 (+CD)

Подробнее

Самоучитель Macromedia Flash 5

Подробнее

 

 
Новости ИТ
01.12.2008  Buffalo выпустил миниатюрные USB-накопители
01.12.2008  VENTO TA-U1 - стильный корпус представлен Asus
01.12.2008  Fujitsu-Siemens выпускает в продажу внешний ускоритель для ноутбуков
01.12.2008  Оригинальные чехлы для ноутбуков от Choiix
01.12.2008  Опубликован код драйвера для беспроводных карт Atheros
01.12.2008  Лучший блог 2008
01.12.2008  Linux запустили на Apple iPhone
01.12.2008  LG KC780
01.12.2008  MSI дополнит линейку Wind-нетбуков двумя моделями
01.12.2008  Nikon D3X - 24,5 млн пикселей для профессионалов
01.12.2008  Киловаттник HIPER M1000 с КПД выше 85%
01.12.2008  AMD впервые снизила цены линейки Radeon HD 4800
01.12.2008  Чистильщики: Wise Registry Cleaner v.3.8.2
01.12.2008  Антивирусы: RemoveIT Pro v4 SE (30.11.2008)
01.12.2008  Корпус ASUS Vento TA-U1 можно поставить вместо новогодней ёлки
01.12.2008  Диагностика: PC Wizard 2008 v.1.871
01.12.2008  Диагностика: NextSensor v.2.7.6.0 Build 1130
01.12.2008  Тестовые приложения: PassMark BurnInTest v.6.0 Build 1000 Beta 15
01.12.2008  Неофициальные драйверы для модемов Motorola
01.12.2008  Драйверы и утилиты для сетевых хранилищ D-Link
 
Полезно

 
Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.
 
Rambler's Top100