Создание простого HTTP-клиента Ernest Avagyan
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 сессии. |
<
br>Методы ( функции ) класса
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; }
Ну вот и всё, приложение готово.
К списку