Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Разработка приложений для Internet

© Александр Фролов, Григорий Фролов
Том 31, М.: Диалог-МИФИ, 1997, 286 стр.

[Назад] [Содеожание] [Дальше]

Приложение ConsoleHTTP

В разделе “Приложение Console FTP“ мы привели приложение ConsoleFTP, которое соединяется с заданным сервером FTP. Для этого в нем сначала создается объект класса CInternetSession, представляющий сеанс работы с WinInet, а затем с помощью метода GetFtpConnection предпринимается попытка соединиться с сервером по заданному адресу. Если установить соединение не удается, например, из - за того, что адрес с таким адресом не существует, то метод GetFtpConnection вызывает исключение CInternetException:


// Инициализируем сеанс работы с WinInet
CInternetSession* m_InternetSession = NULL;
m_InternetSession = new CInternetSession("Connecter");   

try
{
   // Пытаемся соединиться с FTP - сервером sServer
   CFtpConnection*  m_FtpConnection = NULL;
         
   m_FtpConnection = 
         m_InternetSession -> GetFtpConnection( sServer );
   . . .
}
catch (CInternetException* pEx)
{
      // Обрабатываем исключение CInternetException
      . . . 
}

Таким образом, о существовании FTP сервера sServer можно судить по тому, вызвал ли метод GetFtpConnection исключение CInternetException. Если исключение не появилось и метод GetFtpConnection вернул не нулевое значение, значит сервер FTP существует и доступен. А если исключение все же было вызвано, значит, скорее всего, сервера с заданным именем просто не существует.

Как вы знаете, класс CInternetSession содержит в себе три метода для соединения с серверами FTP, WWW и Gopher. Это методы GetFtpConnection, GetHttpConnection и GetGopherConnection. Казалось бы достаточно заменить в исходных текстах приложения FtpConnection вызов метода GetFtpConnection на GetHttpConnection и вы сможете использовать это же приложения для поиска серверов WWW.

Проведите небольшой эксперимент и внесите в исходные тексты проекта FtpConnection соответствующие изменения:


// Инициализируем сеанс работы с WinInet
CInternetSession* m_InternetSession = NULL;
m_InternetSession = new CInternetSession("Connecter");   

try
{
   // Пытаемся соединиться с WWW - сервером sServer
   CHttpConnection*  m_HttpConnection = NULL;
         
   m_HttpConnection = 
         m_InternetSession -> GetHttpConnection( sServer );
   . . .
}
catch (CInternetException* pEx)
{
      // Обрабатываем исключение CInternetException
      . . . 
}

Постройте проект и запустите полученное приложение. Попытайтесь соединиться с помощью приложения с различными серверами WWW. Очень скоро вы обнаружите что соединение выполняется “успешно” даже с несуществующими серверами.

Чтобы быть уверенным, что сервер WWW по данному адресу не существует или недоступен, вы можете попытаться связаться с ним при помощи любой программы навигатора, например с помощью Microsoft Internet Explorer или Netscape Navigator.

Так, если вы запустите навигатор Microsoft Internet Explorer и попробуете просмотреть с его помощью сервер FTP по адресу http://home.frolov.com/, то скорее всего, получите предупреждающее сообщение, показанное нами на рисунке 3.1. Увы, пока сервера FTP с таким именем не существует.

Рис. 3.1. Сервер http://home.frolov.com/ не найден

Изменим теперь приложение ConsoleFtp так, чтобы оно не только соединялось с сервером WWW, но и запрашивало с него информацию об определенном файле. Создайте новый проект ConsoleHttp таким же образом, как вы создавали проект ConsoleFtp. Укажите что приложение будет использовать библиотеку классов MFC.

Затем создайте новый текстовый файл ConsoleHttp.cpp, наберите в нем программный код, представленный в листинге 3.2 и включите его в проект. Чтобы ускорить набор файла ConsoleHttp.cpp за его основу вы можете взять файл ConsoleFtp.cpp из проекта ConsoleFtp.

Листинг 3.2. Файл ConsoleHttp.cpp


//============================================================
// Приложение ConsoleFtp. Выполняет соединение с заданным 
// сервером WWW 
//
// (C) Фролов Г.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
//============================================================

// Включаемый файл для библиотеки MFC
#include <afx.h>
// Включаемый файл для классов WinInet
#include <afxinet.h>
// Включаемый файл для консольного ввода/вывода
#include <iostream.h>
// Включаемый файл для стандартных функций
#include <stdlib.h>

//============================================================
// Главная функция приложения
//============================================================
int main(int argc, char* argv[])
{
   // Если приложение запущено без параметра, отображаем на 
   // экране формат вызова приложения
   if (argc != 2)
   {
      cout << "Programm format: ConsoleFtp <URL>" << endl;
      cout << "  <URL> - URL address of WWW" << endl << endl;
      return -1;
   }

   // Записываем параметр, указанный при вызове приложения в 
   // текстовую строку sUrlAdress. Он должен содержать URL 
   // адрес сервера WWW 
   CString sUrlAdress;
   sUrlAdress = argv[1];
   
   // Отображаем адрес URL на экране
   cout << "URL address: " << sUrlAdress << endl << endl;

   // Определяем переменные, в которые будут записаны 
   // отдельные компоненты адреса, указанного пользователем.
   // Имя сервера
   CString sServer;    
   // Имя объекта на который указывает адрес URL 
   CString sObject;
   // Номер порта
   INTERNET_PORT nPort;
   // Тип сервиса или тип протокола
   DWORD dwServiceType;
   
   // Разбираем адрес URL, записанный в строке sUrlAdress
   if (!AfxParseURL(sUrlAdress, dwServiceType, sServer, 
                    sObject, nPort))
   {
      // В случае ошибки выводим сообщение и завершаем 
      // работу приложения
      cout << "AfxParseURL Error" << endl;
      
      return -1;
   }

   // Проверяем, соответствует ли указанный адрес URL 
   // серверу WWW. Для этого тип сервиса должен быть http://
   if(dwServiceType != AFX_INET_SERVICE_HTTP)
   {
         cout << "URL Address not WWW server" << endl;
         return -2;
   }

   // Указатель на объект класса CInternetSession
   CInternetSession* m_InternetSession = NULL;

   // Инициализируем сеанс работы с WinInet - создаем объект 
   // класса CInternetSession 
   m_InternetSession = new CInternetSession("Connecter");   

   // Запрос для сервера WWW       
   CHttpFile* pFile=NULL;   

   // Определяем указатель на объект класса CFtpConnection
   CHttpConnection*  m_HttpConnection = NULL;

   // Исключения, вызванные в этом блоке try обрабатываются 
   // следующим блоком catch
   try
   {
      // Пытаемся соединииться с сервером sServer
      m_HttpConnection = 
         m_InternetSession -> GetHttpConnection( sServer );
      // Формируем запрос
      pFile = 
         m_HttpConnection -> OpenRequest(
                              CHttpConnection::HTTP_VERB_HEAD,
                              sObject
                             );

      // Дополнительный заголовок запроса HTTP 
      CString headerInfo(_T(
         "Accept: text/*\r\nUser-Agent: HTTP Example\r\n"));
   
      // Добавляем к запросу дополнительный заголовок
      if(pFile->AddRequestHeaders(headerInfo))
      {
         // Передаем запрос на сервер
         if(pFile->SendRequest())
         {
            DWORD dwReturnCode;

            // Определяем код завершения запроса
            if(pFile->QueryInfoStatusCode(dwReturnCode))
            {
               // Отображаем код завершения запроса на экране
               CString sMessage;
               sMessage.Format("%d",dwReturnCode);
               cout << "SendRequest: " << sMessage << 
                        endl << endl;
            }
            else
            {
               cout << "Request Error" << endl << endl;
            }
         }
      }
   
   }
   // Обрабатываем исключение CInternetException
   catch (CInternetException* pEx)
   {
      // Временный буфер для сообщения
      TCHAR szErr[1024];  

      // Выводим сообщение об ошибке
      if (pEx->GetErrorMessage(szErr, 1024))
         cout << "Error: " << szErr <<endl <<endl;

      // Удаляем иссключение
      pEx->Delete();
   }

   // Закрываем запрос
   pFile -> Close();
   // Удаляем объект pFile из памяти
   if(pFile)
      delete pFile;

   // Закрываем соединение с сервером WWW 
   m_HttpConnection -> Close();

   // Удаляем объект m_FtpConnection
   delete( m_HttpConnection );

   // Завершаем сеанс связи
   m_InternetSession -> Close();

   // Удаляем объект m_InternetSession
   delete(m_InternetSession);

   return 0;
}

Постройте только что созданный проект и запустите приложение, указав ему в качестве параметра адрес WWW страницы. Адрес должен быть полным - он должен определять метод доступа (в данном случае http://), имя сервера на котором страница расположена и полный путь к файлу страницы WWW.

Приложение соединится с сервером, на котором эта страница расположена и запросит о ней информацию. Результаты запроса будут выведены на экран. На рисунке 3.3 мы показали работу приложения ConsoleHttp, запросив с сервера http://dials.ccas.ru/ страницу с именем frolov.htm.

Рис. 3.2. Приложение ConsoleHttp запрашивает страницы http://www.frolov.ru/~frolov и http://www.glasnet.ru/~frolov

Устройство приложения ConsoleHttp

Рассмотрим теперь устройство приложения ConsoleHttp. Первая часть приложения ConsoleHttp практически совпадает с приложением ConsoleFtp, работа которого была рассмотрена нами выше в разделе “Устройство приложения ConsoleFtp”. Поэтому мы не будем на нем останавливаться. Скажем только что функция main сначала проверяет командную строку приложения. Если приложение было вызвано без параметров, или их больше чем один, то на экран выводится информация о формате вызова приложения.

Если пользователь правильно указал в командной строке ConsoleHttp только один параметр, он записывается в строку sUrlAdress и отображается на экране:


CString sUrlAdress;
sUrlAdress = argv[1];

Далее строка sUrlAdress передается для обработки функции AfxParseURL, которая разбирает адрес, записанный в ней на составные части и помещает их в переменные dwServiceType, sServer, sObject и nPort:


DWORD dwServiceType; // Тип сервиса
CString sServer;     // Имя сервера
CString sObject;     // Имя объекта на который указывает адрес
INTERNET_PORT nPort; // Номер порта 

if(!AfxParseURL( sUrlAdress, dwServiceType, 
                 sServer, sObject, nPort))
{
   cout << "AfxParseURL Error" << endl;
   return -1;
}

В том случае если пользователь указал в командной строке приложения неправильный адрес, не соответствующий формату URL, то функция AfxParseURL возвращает нулевое значение. Наше приложение отображает сообщение об ошибке и завершается с кодом -1.

Далее приложение проверяет, что указанный адрес соответствует адресу сервера WWW. В этом случае тип сервиса dwServiceType должен быть равен значению AFX_INET_SERVICE_HTTP. Если это не так, например, вместо адреса сервера WWW вы указали адрес сервера FTP, который начинается с ftp://, то на экран будет выведено сообщение об ошибке и приложение завершится с кодом возврата -2:


if(dwServiceType != AFX_INET_SERVICE_HTTP)
{
   cout << "URL Address not WWW server" << endl;
   return -2;
}

Если адрес сервера указан верно, приложение создает сеанс связи с Internet. Для этого мы объявляем указатель на объект класса CInternetSession а затем создаем сам объект, используя функцию new:


// Указатель на объект класса CInternetSession
CInternetSession* m_InternetSession = NULL;

// Инициализируем сеанс работы с WinInet - создаем объект 
// класса CInternetSession catch
m_InternetSession = new CInternetSession("WWW Connecter");   

Конструктор класса CInternetSession имеет много параметров, но мы все их используем по умолчанию, кроме первого параметра, через который мы задаем имя сеанса - WWW Connecter.

Теперь можно попытаться соединиться с сервером и запросить с него файл. Для этого следует вызвать метод GetHttpConnection класса CInternetSession только что созданного объекта m_InternetSession. В случае ошибки этот метод может вызвать исключение CInternetException. Поэтому вызов метода GetHttpConnection мы помещаем в блок try и определяем для него соответствующий блок catch, обрабатывающий исключение CInternetException:


// Исключения, вызванные в этом блоке try обрабатываются 
// следующим блоком catch
try
{
   // . . .
}
catch (CInternetException* pEx)
{
   // . . .
}

Обработчик исключения CInternetException вызывает метод GetErrorMessage для полученного исключения, который записывает во временный буфер szErr причину вызова исключения. Если текстовое описание исключения доступно, метод GetErrorMessage возвращает ненулевое значение. В этом случае мы отображаем полученный текст на экране.

Затем мы завершаем обработку исключения, вызывая для него метод Delete. Он удаляет объект, представляющий исключение из оперативной памяти:


// Удаляем исключение
pEx->Delete();

Рассмотрим блоку try более подробно. В нем приложение исполняет свою главную задачу - пытается установить соединение с сервером WWW. Адрес сервера WWW передается методу GetHttpConnection класса CInternetSession. Если соединение с сервером будет установлено, то этот метод вернет указатель на объект класса GetHttpConnection и мы запишем его во временную переменную m_HttpConnection:


// Определяем указатель на объект класса CHttpConnection
CHttpConnection*  m_HttpConnection = NULL;
// Пытаемся соединиться с сервером sServer
m_HttpConnection = 
   m_InternetSession -> GetHttpConnection( sServer );

Далее приложение HttpConnection вызывает для установленного соединения метод OpenRequest. Первый параметр метода OpenRequest определяет действие. В данном случае в качестве этого параметра используется константа HTTP_VERB_HEAD, означающая что мы желаем только получить информацию о объекте указанном во втором параметре метода sObject:


CHttpFile* pFile=NULL;   
. . .
pFile = m_HttpConnection -> 
   OpenRequest( CHttpConnection::HTTP_VERB_HEAD, 
                sObject 
              );

Если метод OpenRequest завершился успешно, он возвращает указатель на объект класса CHttpFile. Далее вы можете использовать методы класса CHttpFile чтобы узнать данные полученные с сервера. Если вам надо получить с сервера файл, вы должны использовать вместо команды HTTP_VERB_HEAD команду HTTP_VERB_GET.

Сначала для объекта pFile вызывается метод AddRequestHeaders. Он добавляет дополнительный заголовок headerInfo к запросу HTTP. Сам заголовок headerInfo содержит текстовую строку, в которой определяются тип файлов, которые может принимать наше приложение и его имя:


CString headerInfo( 
    _T("Accept: text/*\r\nUser-Agent: Console Http\r\n"));

if(pFile->AddRequestHeaders(headerInfo))
{
}

Если метод AddRequestHeaders завершился без ошибок, он возвращает ненулевое значение. В этом случае наше приложение вызывает метод SendRequest, который и передает сформированный запрос серверу WWW. Если запрос передан успешно, то метод SendRequest возвращает ненулевое значение. В противном случае возвращается нуль. В случае возникновения ошибок метод SendRequest может вызвать исключение CInternetException:


if(pFile->SendRequest())
{
}

Обратите внимание, что исключение CInternetException может также быть вызвано методом GetHttpConnection. Мы поместили эти методы в один блок try, поэтому исключение вызванное любым из них будет обработано одним и тем же блоком catch.

Метод SendRequest только передает запрос серверу. Чтобы получить от него ответ, мы обращаемся к методу QueryInfoStatusCode. Этот метод получает код состояния, связанный с последним запросом к серверу и записывает его во временную переменную dwReturnCode.

Если метод QueryInfoStatusCode отработал успешно, он возвращает ненулевое значение. В этом случае мы преобразуем значение dwReturnCode в текстовую форму и отображаем его на экране:


DWORD dwReturnCode;
if(pFile -> QueryInfoStatusCode(dwReturnCode))
{
   CString sMessage;
   sMessage.Format("%d",dwReturnCode);

   cout << "SendRequest: " << sMessage << endl << endl;
}

Больше приложение ConsoleHttp ничего не делает с сервером, поэтому закрываем объект pFile и удаляем его из памяти:


pFile->Close();
if(pFile)
   delete pFile;

Затем закрываем соединение с сервером и удаляем соответствующий объект m_HttpConnection из памяти (этот объект создается методом GetHttpConnection):


// Закрываем соединение с сервером WWW 
m_HttpConnection -> Close();
// Удаляем объект m_HttpConnection
delete( m_HttpConnection );

Перед окончанием работы приложения, завершаем сеанс связи, вызывая метод Close класса CInternetSession, а затем удаляем соответствующий объект из памяти, вызывая функцию delete:


// Завершаем сеанс связи
m_InternetSession -> Close();
// Удаляем объект m_InternetSession
delete(m_InternetSession);
[Назад] [Содеожание] [Дальше]