И снова dns
В прошлой части статьи говорилось о том, что для соединения socket’а с сервером Вы можете использовать функцию connect, но эта функция требует структуру sockaddr (или sockaddr_in в случае TCP/IP). Как нам получить эту структуру? Sockaddr_in требует адресное семейство, IP адрес и порт сервера. Адресное семейство понятно - AF_INET, порт тоже не трудно указать (порт по умолчанию для HTTP протокола, 80), а вот как быть с IP адресом, если мы знаем только название сайта, куда мы хотим подключиться? Помните, я говорил про DNS? Вот тут как раз в дело вступает эта система. Что бы узнать адрес сервера достаточно воспользоваться функцией gethostbyname: hostent * gethostbyname(const char *name);
В функцию вы передаете строку с названием сайта (например, www.vr-online.ru), а функция вернет указатель на структуру hostent. В этой структуре будет содержаться список адресов, доступных для данного сайта. Один из этих адресов можно подставить в структуру sockaddr_in и будет Вам счастье.
Основа
Программа, которую мы напишем, будет соединяться с сервером, отправлять HTTP HEAD запрос и сохранять все, что пришло в ответ. Сначала определимся с «базой» программы, т.е. с тем, что нам необходимо для корректной работы (да и работы вообще) нашего приложения.
#include <iostream>
#include <winsock2.h>
#include <windows.h>
using namespace std;
class HeadReqException
{
public:
HeadReqException() :
m_pMessage("") {}
virtual ~HeadReqException() {}
HeadReqException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
Большинство WinSock функций включено в библиотеке windows.h, но т.к. мы будем использовать некоторые специализированные функции, то необходимо подключить и winscok2.h. Для чего нужна библиотка iostream и что за строчка «using namespace std;» я объяснять не буду, т.к. делал это ранее в своих статьях. Класс HeadReqException нужен для обработки возникающих ошибок. Один из конструкторов этого класса принимает строку с сообщением ошибки, которая может быть извлечена с помощью функции what(). O спецификаторах доступа и виртуальных функциях я сейчас рассказывать не буду, т.к. это выходит далеко за пределы этого материала и по этим темам можно писать отдельную статью (хотя зачем? множество информации по ним Вы сможете найти в Интернете).
Константы и глобальные переменные
В программе потребуются некоторые константы и глобальные данные, которые описаны в следующем куске кода:
const int REQ_WINSOCK_VER = 2; // Минимальная версия WinSock
const char DEF_SERVER_NAME[] = "www.vr-online.ru";
const int SERVER_PORT = 80;
const int TEMP_BUFFER_SIZE = 128;
const char HEAD_REQUEST_PART1[] =
{
"HEAD <a href="http://vr-online.ru/articles">http://vr-online.ru/articles</a> HTTP/1.1\r\n" // Получаем страницу
"Host: " // Определяем имя сервера
};
const char HEAD_REQUEST_PART2[] =
{
"\r\n" // Конец строки, содеражщей имя сервера
"User-agent: HeadReqSample\r\n" // Определяем браузер
"Connection: close\r\n" // Закрываем соединение после ответа
"\r\n" // Пустая строка говорит о том, что ответ получен полностью
};
typedef unsigned long IPNumber;
Назначение первых четырех переменных понятно из их имени. Далее полный HTTP запрос разбивается на две части, каждая из которых представляет собой массив строк. Изначально массив заполнен. Ну и последняя строка, как и четыре первых, не должна вызывать каких-либо непоняток.