...
...

Создание "пауков" для World Wide Web

Создание "пауков" для World Wide Web

Когда-то разработка приложений для Интернета требовала от программистов знания различных протоколов, методики работы с сокетами и некоторых других вещей. Теперь же, для того чтобы добавить в приложение возможность серфинга по World Wide Web, достаточно потратить всего лишь несколько минут. В данной статье рассматривается процесс создания небольшого "паука" — программы, которая скачивает из Всемирной паутины некоторые данные. Описание той части этого процесса, которая не относится к Интернету, пропущено. Зато сделан акцент на деталях, которые можно использовать для разработки других интернет-приложений.

При написании исходного кода программы использовался Internet API (Internet Application Programming Interface — Интерфейс программирования приложений для Интернета). Функции этого интерфейса экспортируются из динамически связываемой библиотеки wininet.dll. Они подразделяются на четыре группы — общие, FTP-, HTTP- и Gopher-функции — и позволяют работать с протоколами FTP, HTTP и Gopher, не задумываясь над многими техническими особенностями этих протоколов.

Общие функции позволяют работать с перечисленными выше протоколами просто задав URL. С помощью этих функций доступ к Интернету превращается в простой процесс, который делится на три этапа:
1. Получение дескриптора нужного URL'а посредством одной функции.
2. Использование другой функции для чтения данных, расположенных по адресу, URL которого был указан.
3. Освобождение дескриптора, полученного на первом этапе, с помощью третьей функции.
Кроме перечисленных действий, общие функции позволяют выполнять еще некоторые, которые будут рассмотрены в этой статье.
FTP-, HTTP- и Gopher-функции предназначены для работы с тремя самыми популярными протоколами WWW. Множества этих функций частично пересекаются, т.е. некоторые функции встречаются одновременно в нескольких группах, к примеру, функция InternetConnect, которая создает соединение со Всемирной сетью. Также существуют специфичные для каждого протокола функции, что обусловлено логикой функционирования протоколов. К примеру, FTP позволяет манипулировать каталогами, тогда как HTTP лишен этой возможности.
Перейдем к процессу создания нашего "паука". Для того чтобы все функционировало так, как ожидается, необходимо, чтобы на компьютере был установлен браузер, совместимый с Microsoft Internet Explorer 3.0. При использовании Internet API браузеру отводится очень важная роль — он обеспечивает доступ в Интернет, а также выполняет другую работу, необходимую для нормального серфинга по Сети.

Рассмотрим следующий фрагмент исходного кода:
#include <wininet.h>

char cBuffer[1024];
DWORD dwBytesRead;

HINTERNET hInternetSession = InternetOpen("Spider", INTERNET_OPEN_ TYPE_PRECONFIG, NULL, NULL, 0);
HINTERNET hU RL = InternetOpenUrl (hInter netSession, "http://nestor.minsk.by/kg", NULL, 0, 0, 0);
BOOL boResult = InternetReadFile (hURL, (LPSTR)cBuffer, (DWORD) 1024, &dwBytesRead);
InternetCloseHandle(hURL);
InternetCloseHandle(hInternetSession);

Этих строк достаточно, чтобы прочитать первый килобайт web-страницы, расположенной по адресу http://nestor.minsk.by/kg. Ознакомимся с функциями, использованными в приведенном фрагменте, более подробно.
Функция InternetOpen инициализирует использование Internet API, и поэтому должна вызываться самой первой. Она заставляет wininet.dll инициализировать внутренние структуры данных и подготовиться к последующим вызовам других функций. Приложение может вызывать InternetOpen любое количество раз, хотя достаточно и одного. Необходимость в неоднократном вызове может возникнуть, если в программе требуется использовать различные proxy-серверы. При вызове функции передаются следующие параметры:
LPCTSTR lpszAgent — указатель на строку, которая содержит название программы, вызывающей интернет-функции. При работе с протоколом HTTP это название используется в качестве идентификатора агента пользователя (User Agent).
DWORD dwAccessType — параметр, задающий тип требуемого доступа. Может принимать различные значения, но для простой программы вполне подойдет значение INTERNET_OPEN_ TYPE_PRECONFIG, которое сообщает функции, что информацию о соединении с Интернетом следует взять из реестра.
LPCTSTR lpszProxyName — указатель на строку, содержащую одно или несколько имен proxy-серверов, которые необходимо использовать в случае надобности. Если значение dwAccessType равно INTERNET_OPEN_TYPE_PRECONFIG, то параметр lpszProxyName игнорируется и должен иметь значение NULL.
LPCTSTR lpszProxyBypass — указатель на строку с адресами хостов, которые не должны использовать proxy-серверы. Поскольку этот параметр не используется, когда dwAccessType равен INTERNET_OPEN_TYPE_PRECONFIG, то в нашем случае lpszProxyBypass, как и lpszPro-xyName, должен иметь значение NULL.
DWORD dwFlags — параметр, содержащий флаги, которые задают поведение функции. Может быть комбинацией следующих значений: INTERNET_FLAG_ASYNC — указывает, что все дескрипторы, полученные на основе дескриптора, возвращенного функцией InternetOpen, будут делать только асинхронные запросы; INTERNET_FLAG_FROM_CACHE — обращение к Интернету не производится, и все данные поставляются из кэша (в случаях, когда кэш не содержит требуемых данных, будет возвращаться соответствующая ошибка, к примеру, ERROR_FILE_NOT_FOUND); INTERNET_FLAG_OFFLINE — идентично INTERNET_FLAG_FROM_CACHE. Если не задан ни один из перечисленных флагов (dwFlags равен нулю), запросы к ресурсам Интернета будут инициировать реальное соединение с ними.
В случае успешного выполнения функция InternetOpen возвращает дескриптор, который должен использоваться в вызовах других функций Internet API. В противном случае возвращается значение NULL, а информация об ошибке может быть получена посредством функции GetLastError.
Следующая функция рассматриваемого фрагмента — Inter-netOpenUrl. Она открывает требуемый ресурс. Эта функция особенно полезна тогда, когда приложению требуется только получить данные, находящиеся по определенному URL'у. Функции передаются следующие параметры:
HINTERNET hInternet — дескриптор, полученный посредством вызова InternetOpen.
LPCTSTR lpszUrl — указатель на строку, которая должна содержать URL ресурса. В настоящее время поддерживаются URL'ы, начинающиеся на "ftp:", "http:", "https:" и "gopher:".
LPCTSTR lpszHeaders — указатель на строку, содержащую заголовки, отправляемые HTTP-серверу. Задавать эти заголовки необязательно, а потому подойдет значение NULL.
DWORD dwHeadersLength — параметр, задающий длину (в TCHAR'ах) заголовков. Если он равен -1, а lpszHeaders не равен NULL, то подразумевается, что строка, на которую указывает lpszHeaders, заканчивается нулем (ASCIIZ). В этом случае длина строки вычисляется автоматически.
DWORD dwFlags — параметр, являющийся комбинацией почти двух десятков различных флагов, определяющих режимы работы с открываемым ресурсом. В случае, если значения 0 недостаточно для нормальной работы, можно получить полное описание всех флагов этого параметра, обратившись к соответствующей документации.
DWORD_PTR dwContext — указатель на переменную типа DWORD, содержащую дополнительную информацию, которая передается вместе с дескриптором ресурса. Если передавать дополнительную информацию нет нужды, следует присвоить этому параметру значение 0.
InternetOpenUrl возвращает дескриптор FTP-, HTTP- или Gopher-ресурса, если соединение было установлено, или NULL, если соединение установить не удалось. В последнем случае функция GetLastError возвратит информацию об ошибке.
Следует отметить, что Internet-OpenUrl не работает с протоколом Gopher при использовании порта, меньшего, чем 1024, за исключением 70-го — стандартного порта — и 105-го — используемого Central Services Organization для поиска имен.
После того как получен дескриптор открытого ресурса, можно производить чтение данных с помощью функции InternetReadFile, которая считывает ресурс в виде потока байтов. Если дескриптор был получен функцией InternetOpenUrl, то чтение ресурса уподобляется скачиванию файла, что сделано специально для упрощения процесса программирования. При этом для некоторых типов информации, как, например, содержимое каталога в FTP, производится преобразование данных в HTML-поток. Это делается построчно. К примеру, содержимое каталога может быть преобразовано в строки формата HTML и уже преобразованным возвращено функцией Internet ReadFile.
Функции InternetReadFile передаются следующие параметры:
HINTERNET hFile — дескриптор открытого ресурса. Кроме Internet OpenUrl, этот дескриптор может быть получен посредством вызова функций FtpOpenFile, GopherOpenFile или HttpOpenRequest.
LPVOID lpBuffer — указатель на буфер, в котором будут сохранены прочитанные данные.
DWORD dwNumberOfBytesToRead — параметр, сообщающий функции, какой объем данных (в байтах) необходимо прочитать.
LPDWORD lpdwNumberOfBytesRead — указатель на переменную типа DWORD, в которую функция помещает значение количества прочитанных байтов данных.

В случае успешного завершения функции она возвратит значение TRUE. В противном случае будет возвращено значение FALSE, а с помощью GetLastError можно будет получить информацию об ошибке. Также некоторая информация будет доступна посредством вызова функции InternetGetLastResponseInfo, описание которой представлено далее.
Поскольку объем данных, которые необходимо прочитать, может быть неизвестен заранее, их можно считывать по частям, вызывая InternetReadFile до тех пор, пока она возвращает значение TRUE, а переменная, в которую заносится объем прочитанных данных, не станет равной нулю.
После того как исчезнет надобность в использовании дескрипторов, полученных функциями InternetOpen и InternetOpenUrl, их следует закрыть, вызвав InternetCloseHandle. Именно это и делают две последние строки приведенного выше фрагмента. При вызове функции InternetCloseHandle передается только один параметр — дескриптор, который необходимо закрыть. Как и предыдущие функции, InternetCloseHandle возвращает TRUE, если ее вызов был выполнен успешно, и FALSE — если по какой-то причине закрыть дескриптор невозможно. В случае неудачи ее причину можно определить, проанализировав значение, возвращаемое функцией GetLastError.
Мы рассмотрели минимум функций, которого достаточно для того, чтобы создавать приложения, способные проверять, обновились ли определенные web-страницы, сканировать Интернет с целью сбора e-mail'ов, скачивать mp3-файлы и выполнять другие действия, основанные на получении данных. Ознакомимся теперь еще с тремя функциями, которые позволят немного расширить возможности работы со Всемирной сетью.

InternetGetLastResponseInfo — эта функция возвращает описание ошибки или ответа сервера, которые могли возникнуть как результат выполнения какой-либо функции Internet API. InternetGetLastResponseInfo объявлена следующим образом:
BOOL InternetGetLastResponseInfo
(
LPDWORD lpdwError,
LPTSTR lpszBuffer,
LPDWORD lpdwBufferLength
);

LPDWORD lpdwError — это указатель на переменную типа DWORD, в которую будет помещен код ошибки.
LPTSTR lpszBuffer — указатель на буфер, в котором будет сохранено текстовое сообщение об ошибке или ответ сервера.
LPDWORD lpdwBufferLength — указатель на переменную типа DWORD, содержащую размер буфера в TCHAR'ах. После выполнения функция заносит в эту переменную размер строки, помещенной в буфер, без учета завершающего нулевого символа.
Функция возвращает TRUE в случае удачи и FALSE в противном случае. При возникновении ошибки дополнительную информацию можно получить функцией GetLastError. Если буфер имеет недостаточный размер, для того чтобы вместить текстовое сообщение, GetLastError возвратит значение ERROR_INSUFFICIENT_BUFFER, а lpdwBufferLength будет содержать длину сообщения. Следует обратить внимание на то, что буфер должен быть достаточного размера, чтобы включить не только сообщение, но и завершающий нулевой символ, который не учитывается при заполнении переменной lpdwBufferLength.
В некоторых случаях протоколы FTP и Gopher могут возвращать дополнительную текстовую информацию. Эта информация может быть получена с помощью InternetGetLastResponseInfo, если GetLast-Error возвратила значение ERROR_INTERNET_EXTENDED_ERROR.

Следующая функция проверяет наличие соединения с Интернетом и, если оно отсутствует, может попытаться его установить. Название этой функции — InternetCheckConnection. Ее объявление имеет следующий вид:
BOOL InternetCheckConnection
(
LPCTSTR lpszUrl,
DWORD dwFlags,
DWORD dwReserved
);

LPCTSTR lpszUrl — указатель на строку, содержащую URL, который будет использоваться для проверки соединения.
DWORD dwFlags — параметр, содержащий флаги, которые задают режимы работы функции. В настоящее время используется только один флаг — FLAG_ICC_FORCE_CONNECTION. Если он установлен, функция пытается установить соединение, ориентируясь по значению предыдущего параметра: если lpszUrl не равен NULL, то InternetCheckConnection пингует хост, указанный в URL'е, в противном случае пингуется ближайший сервер, информация о котором присутствует во внутренней базе данных операционной системы.
DWORD dwReserved — этот параметр зарезервирован и должен быть равен нулю.
Функция InternetCheckConnection возвращает TRUE, если соединение удалось установить. Если возвращается значение FALSE, с помощью GetLastError можно определить причину, по которой соединение не может быть установлено.

Напоследок рассмотрим функцию InternetAttemptConnect, которая позволяет приложению попытаться установить соединение с Интернетом, прежде чем делать какие-либо запросы к ресурсам Сети. Обычно эта функция заставляет систему вывести на экран окно дозвона до провайдера. InternetAttemptConnect объявлена следующим образом:
DWORD InternetAttemptConnect
(
DWORD dwReserved
);

Единственный параметр функции зарезервирован и должен быть равен нулю.
В отличие от предыдущих функций, InternetAttemptConnect возвращает значение типа DWORD. Если оно равно ERROR_SUCCESS, то попытка установить соединение с Интернетом удалась. В противном случае будет возвращен код ошибки.
Итак, мы ознакомились с семью функциями, позволяющими разрабатывать приложения, работающие с Интернетом: Internet Open, InternetOpenUrl, Internet ReadFile, InternetCloseHandle, InternetGetLastResponseInfo, In-ternetCheckConnection и InternetAttemptConnect. Internet API включает в себя еще более 30 других функций, полное описание которых можно получить, обратившись к соответствующей документации.

Сергей Иванчегло,
come-from-beyond@mail.ru



© Компьютерная газета

полезные ссылки
Корпусные камеры видеонаблюдения
IP камеры видеонаблюдения