Глава 21. Создание простого HTTP-клиента
В этой главе будет написана программа, которая может считывать файлы из Internet
по HTTP протоколу и записывать их на диск. Для связи с Internet в Visual C++ существует так называемый WinInet Class. В него входят несколько подклассов. Далее представлены классы WinInet:
Классы | Описание | CInternetSession | Создаёт Internet сессию. Все MFC WinInet приложения должны создавать CInternetSession объект перед использрванием других WinInet классов. | CInternetConnection | Создаёт коннект с Internet. Это базовый класс для классов CFtpConnection, CGopherConnection, и CHttpConnection. | CFtpConnection | Устанавливает соединение по FTP протоколу. | CGopherConnection | Создаёт Gopher коннект. | CHttpConnection | Устанавливает соединение по HTTP протоколу. | CInternetFile | Разрешает удалённый доступ к файлам на Internet серверах. Это базовый класс для классов CGopherFile and CHttpFile. | CGopherFile | Разрешает удалённый доступ к файлам на Gopher серверах. | CHttpFile | Разрешает удалённый доступ к файлам на HTTP серверах. | CFileFind | Разрешает поиск файлов в Internet. Это базовый класс для классов CFtpFileFind and CGopherFileFind. | CFtpFileFind | Разрешает поиск файлов на FTP серверах. | CGopherFileFind | Разрешает поиск файлов на Gopher серверах. | CGopherLocator | Отыскивает Gopher устройство ввода позиций от gopher сервера. | CInternetException | Управляет исключениями, сгенерированными WinInet классом. |
Наша программа будет использовать четыре класса WinInet: CInternetSession, CInternetFile, CHttpFile и CHttpConnection Далее будут описаны методы( функции ) этих классов:
Методы ( функции ) класса CInternetSession
Функции | Описание | Close() | Закрывает Internet сессию. | EnableStatusCallback() | Разрешает использование функции повторного вызова, которая используется для асинхронных действий. | GetContext() | Получает значение контекста Internet сессии. | GetFtpConnection() | Устанавливает подключение по FTP протоколу. | GetGopherConnection() | Устанавливает подключение с Gopher серверами. | GetHttpConnection() | Устанавливает подключение по HTTP протоклолу. | OnStatusCallback() | Модифицирует состояние операции. | OpenURL() | Соединяется с данным URL. | QueryOption() | Сервис проверки ошибки провайдера. | ServiceTypeFromHandle() | Получает тип сервиса от Internet дескриптора. | SetOption() | Устанавливает опции Internet сессии. |
Методы ( функции ) класса CInternetFile
Функции | Описание | Abort() | Закрывает файл и игнорирует все ошибки. | Close() | Закрывает файл. | Flush() | Сбрасывает файл на диск. | Read() | Счатывает байт из файла. | ReadString() | Считывает строку символов из файла. | Seek() | Переустанавливает указатель внутри файла. | SetReadBufferSize() | Устанавливает размер буфера для чтения. | SetWriteBufferSize() | Устанавливает размер буфера для записи. | Write() | Записывает байт в файл. | WriteString() | Записывает строку с нулевым символом в конце в файл. |
Методы ( функции ) класса CHttpFile
Функции | Описание | AddRequestHeaders() | Добавляет заголовок к HTTP запросу. | Close() | Закрывает CHttpFile объект. | GetFileURL() | Получает URL файла. | GetObject() | Получает объект по HTTP запросу. | GetVerb() | Получает заголовок запроса. | QueryInfo() | Получает ответ или заголовок запроса. | QueryInfoStatusCode() | Получает код состояния HTTP запроса. | SendRequest() | Посылает HTTP запрос. |
Далее напишем код программы и разберём каждую строчку:
...
CString m_url = "mark5.dhtp.kiae.ru"; // имя URL
CString m_mes; // переменная в которой будут хранится сообщения
char temp[100]; // промежуточная переменная для перевода
// данных из Int в char
CString m_path; // имя файла для записи
char strBody[1024]; // буфер из 1024 байт
...
int CHTTP_ClientDlg::OnButtonConnect()
{
// создаём переменную session и открываем сессию ANDY
CInternetSession session( _T( "ANDY" ), PRE_CONFIG_INTERNET_ACCESS );
// создаём переменную pServer класса CHttpConnection
CHttpConnection* pServer = NULL;
// создаём переменную pFile класса CHttpFile
CHttpFile* pFile = NULL;
/*
Обратите внимание, что все запросы к функциям членам WinInet классов включены в блок программы TRY.
Это сделано так, потому что при соединении с каким либо URL есть риск неправильной ссылки,
особенно, когда Вы полагаете, что пользователь сам печатает URL.
Другая проблема - времена ожидания, которые возникают, когда требуемый URL в настоящее время
неспособен обслужить подключение.
Так же обработка WinInet исключений, которые представлены в классе
CInternetException, является важной частью создания Internet приложения под MFC.
*/
try
{
CString strServerName; // имя сервера
CString strObject; // имя объекта
INTERNET_PORT nPort; // номер порта для связи
DWORD dwServiceType; // тип сервиса
// функция AfxParseURL получает данные с указанного URL ( у нас m_url ) об сервере,
// объекте, типе сервиса и порте
if ( AfxParseURL( m_url, dwServiceType, strServerName, strObject, nPort ) == 0 )
{
return 1; // выход из функции OnButtonConnect()
}
// вывод данных о сервере
m_mes = "";
m_mes += "Server Name = ";
m_mes += (CString)strServerName; m_mes += "\r\n";
m_mes += "Object Name = ";
m_mes += (CString)strObject; m_mes += "\r\n";
m_mes += "Port = ";
itoa( nPort, temp, 10 );
m_mes += (CString)&temp[0]; m_mes += "\r\n";
UpdateData( FALSE );
// Устанавливаем подключение по HTTP протоклолу.
pServer = session.GetHttpConnection( strServerName, nPort );
// посылаем запрос об объекте ( strObject )
pFile = pServer->OpenRequest(
CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL,
INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT
);
// Добавляем заголовок к HTTP запросу
pFile->AddRequestHeaders( _T( "Accept: */*\r\nUser-Agent: ANDY\r\n" ) );
// посылаем запрос
pFile->SendRequest( );
DWORD dwRet; // переменная для хранения кода состояния
pFile->QueryInfoStatusCode( dwRet ); // записываем код состояния в dwRet
// вывод данных
m_mes += "The HTTP GET returned a status code of ";
itoa( dwRet, temp, 10 );
m_mes += (CString)&temp[0]; m_mes += "\r\n";
CString strHeader; // переменная для хранения полученного заголовока запроса
pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strHeader); // записываем заголовок в strHeader
// вывод данных
m_mes += "Header = ";
m_mes += strHeader;
UpdateData( FALSE );
// если код состояния не равен 200, то выходим из функции
if( dwRet != 200 ) { m_mes += "Program terminate!"; UpdateData( FALSE ); return 1; }
// ----------------------------------------------------------
// проверка выбора файла для записи
m_mes += "Starting download the file."; m_mes += "\r\n";
if( m_path == "" )
{
m_mes += "Error! No file to save. Choese the file.";
m_mes += "\r\n"; UpdateData( FALSE ); return 1; }
else
{
m_mes += "File name to save : ";
m_mes += m_path; m_mes += "\r\n"; UpdateData( FALSE );
}
CFile file2; // объявляем переменную file2 класса CFile
// открываем файл для записи в двоичном формате ( CFile::typeBinary ) !!!
file2.Open((LPCTSTR)m_path,
CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
int allRead = 0; // переменная для хранения общего числи считанных байт
int nRead = pFile->Read( strBody, 1024 ); // считываем первые 1024 байта в буфер.
// переменная nRead хранит количество
// считанных байт
allRead += nRead; // обновляем общее число считанных байт
// вывод данных
m_mes += "Loading ";
itoa( nRead, temp, 10 );
m_mes += (CString)&temp[0]; m_mes += " bytes"; m_mes += "\r\n";
UpdateData( FALSE );
// записываем буфер из nRead байт в файл
file2.Write( strBody, nRead );
// цикл считывания, пока nRead не будет равняться нулю
while ( nRead > 0 )
{
nRead = pFile->Read( strBody, 1024 );
if( nRead != 0 )
{
m_mes += "Loading ";
itoa( nRead, temp, 10 );
m_mes += (CString)&temp[0]; m_mes += " bytes"; m_mes += "\r\n";
file2.Write( strBody, nRead );
allRead += nRead;
UpdateData( FALSE );
}
}
// вывод данных
m_mes += "\r\n";
m_mes += "Total bytes = ";
itoa( allRead, temp, 10 );
m_mes += &temp[0]; m_mes += "\r\n"; UpdateData( FALSE );
file2.Close(); // закрываем файл
pFile->Close(); // закрываем Internet файл
pServer->Close(); // закрываем сервер
m_mes += "Download is complete !!!"; m_mes += "\r\n";UpdateData( FALSE );
}
catch ( CInternetException* pEx )
{
// Если произошла ошибка в WinInet
// вывод ошибки
char szErr[1024];
pEx->GetErrorMessage( szErr, 1024 );
m_mes += "Error: ( ";
itoa( int(pEx->m_dwError), temp ,10 );
m_mes += (CString)&temp[0];
m_mes += " ) ";
m_mes += (CString)&szErr[0]; m_mes += "\r\n";
UpdateData( FALSE );
pEx->Delete( ); // удаление переменной класса CInternetException
if ( pFile != NULL )
delete pFile; // закрываем Internet файл
if ( pServer != NULL )
delete pServer; // закрываем сервер
session.Close( ); // закрываем сессию
return 1;
}
if ( pFile != NULL )
delete pFile; // закрываем Internet файл
if ( pServer != NULL )
delete pServer; // закрываем сервер
session.Close( ); // закрываем сессию
return 0;
} Ну вот и всё, приложение готово. Отсюда можно взять рабочую программу HTTP Client под MFC, с использованием WinInet.
|