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

В данной главе мы рассмотрим основы загрузки файлов через протокол HTTP с использованием Win API. Документ RFC 2068 (http://itfm.adamant.net/web/rfc2068.html) раскрывает много тонкостей стандарта. Этот документ занимает около 70 страниц. Описывать его весь нет смысла, поэтому рассмотрим основную команду GET, а остальные команды (например, PUT) действуют по такому же принципу

Итак, создадим новое приложение и поместим на форму (рис. 5.12) следующие элементы:

• строку ввода для указания адреса прокси-сервера;
• компонент Memo для отображения полученных от сервера данных;

• кнопку, по нажатии которой будет отправляться запрос.

5.6. HTTP-клиент

Рис. 5.12. Форма будущей программы

Если в вашей сети нег прокси-сервера, то данные, которые мы будем отправлять в этом примере "проксику", надо посылать напрямую нужному веб-серверу (чаще всего это порт 80).

По нажатии кнопки Отправить будет выполняться код из листинга 5.12.

Листинг 5.12. Отправка запроса веб-серверу

procedure TTCPClientForm.btSendClick(Sender: TObject); var wData: WSADATA; sServerListen. TSOCKET: server_addr: sockaddr_in: s: String; begin // Загрузка WinSock

if WSAStartup(MAKEWORDd.l). wData) <> 0 then begin MessageBox(0. 'He могу загрузить WinSock'. 'Ошибка'. 0): exit:

end:

// Создание сокета

sServerListen := socket(AF_INET. SOCK_STREAM. IPPROTOJP):
if sServerListen = INVALID_SOCKET then
begin
MessageBox(0. 'Ошибка создания сокета'. 'Ошибка', 0);
exit;
end;

// Заполнение структуры адреса server_addr.sin_addr.s_addr := htonl(INADDR_ANY): serwer_addr.sin_family •= AF_INET; server_addr sin_port := htons(8080): server_addr.sin_addr := LookupName(edServer Text).

if (connect(sServerListen. server_addr. sizeof(server_addr)) =
SOCKETJRROR) then
begin
TestWi nSockError('Connect');
exit:
end:

// Посылаем запрос серверу

s :- 'GET http://www.vr-online.ru/.HnP/1.0,+#13#10+
'Host: www.vr-online.ru'+#13#10. SendStrCsServerListen. s):

// Ожидание 2 секунды Sleep(2000).

// Чтение полученных данных GetStrCsServerListen):

CloseSocket(sServerListen);
end:

Код уже должен быть понятен, но все же требует некоторых пояснений. В листинге я опустил описание уже знакомых нам функции SendStr, GetStr, LookupName. Реализацию этих функций можно взять из предыдущих примеров. Как видите, мы просто соединяемся с веб-сервером на порт 80 и отправляем ему следующий HTTP-запрос:

GET http://www.vr-onHne.ru/ HTTP/1.0 Host: www vr-online.ru

Первая строка запрашивает загрузку веб-страницы с сайта www.vr-online.ru, во второй строке указывается сайт, с которого должна происходить загрузка. Этого достаточно, чтобы сервер возвратил полное содержимое нужной веб-страницы в текстовом режиме.

Прежде чем получать данные, для простоты примера делается задержка в 2 секунды. За это время веб-сервер успевает передать нам все данные, и они сохраняются в буфере. После вызова функции recv сразу же считывается весь файл. Если не сделать задержку, то в буфере будут находиться не все данные и мы должны будем вызывать функцию recv в цикле и получать данные порциями, по мере их поступления. Этот цикл выполняется до тех пор, пока функция не вернет пустое значение, то есть передача завершена и соединение закрыто.

В функции GetStr должен быть выделен буфер достаточного размера для приема всего файла. Если размер не позволит принять весь файл, то придется вызывать функцию несколько раз. Такой способ хорош для простого примера, но в реальном приложении я рекомендую использовать цикл, потому что сервер может ответить с задержкой и в первом пакете вы получите неполные данные. При использовании цикла очень лепсо определить конец передачи данных и корректно получить все необходимое.

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

Так как разбор HTML-файлов происходит на компьютере клиента, это позволяет экономить трафик и загружать только то, что необходимо. При этом возрастает нагрузка на веб-сервер из-за множества подключений. Один клиент может создавать по 10 и более соединений для загрузки всех необходимых файлов.

Наиболее "умные" браузеры создают подключения с сервером параллельно. Например, если на веб-странице найдено 10 картинок, то они будут загружаться одновременно. По моим наблюдениям, Internet Explorer одновременно загружает по 3 изображения. Это нагружает сервер, но оптимизирует трафик и скорость загрузки на клиенте.

При отправке запроса мы указываем стандарт НТТР/1.0, но существует более новая версия НТТР/1.1. Про различие между двумя стандартами вы можете прочитать в соответствующем RFC.

Меня очень часто спрашивают, как написать программу, которая будет "кликать" по определенной кнопке? Нужно всего лишь выяснить, какой запрос посылается серверу, и отправлять его из своей программы Если вы не знаете, как браузер формирует запрос, то лучше его подсмотреть. Это можно сделать с помощью локального прокси-сервера, описанного в главе 4.

Зная запрос, который должен быть отправлен серверу, легко можно отправить его из своей программы, имитировав при этом отправку от имени другой программы, ведь отправитель идентифицируется с помощью содержащейся в запросе информации.

ПРИМЕЧАНИЕ -

Исходный код рассмотренного здесь примера находится на компакт-диске в каталоге 5оигсеБ\сг|05\НТТРСНеп1.

5.5. Создание Proxy-сервера || Оглавление || 5.7. Широковещание


Delphi в шутку и всерьез: что умеют хакеры



Новости за месяц

  • Январь
    2022
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс