Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
чтиво_ч3.doc
Скачиваний:
4
Добавлен:
15.11.2019
Размер:
435.2 Кб
Скачать

Функция main()

Первое что необходимо сделать – инициализировать socket. Мы сделаем это в главной функции программы.

int _tmain(int argc, _TCHAR* argv[])

{

WSADATA wsaData;

 

cout << "Initializing winsock... ";

 

if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER,0), &wsaData)==0)

{

// Проверяем подходит ли нам максимально доступная версия WinSock

if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)

{

cout << "initialized.\n";

 

// Устанавливаем имя хоста:

const char *pHostname = DEF_SERVER_NAME;

 

RequestHeaders(pHostname);

}

else

{

cerr << "required version not supported!";

}

 

cout << "Cleaning up winsock... ";

 

// Очищаем socket

if (WSACleanup()!=0)

{

cerr << "cleanup failed!\n";

}

cout << "done.\n";

}

else

{

cerr << "startup failed!\n";

}

return 0;

}

Сначала вызываем WSAStartup. Эта функция получает максимально возможную для данной программы версию WinSock и заполняет структуру WSADATA. После мы проверяем, успешно ли выполнилась эта функция и сравниваем версию WinSock с минимально допустимой версией. Если функция WSAStartup завершилась удачно, то необходимо вызвать WSACleanup, что и делается в конце кода.

RequestHeaders

RequestHeaders – это функция, в которой и происходит вся магия. Основа этой функции выглядит следующим образом:

bool RequestHeaders(const char *pServername)

{

SOCKET hSocket = INVALID_SOCKET;

char tempBuffer[TEMP_BUFFER_SIZE];

sockaddr_in sockAddr = {0};

bool bSuccess = true;

 

try

{

//тут код

}

catch(ClientException e)

{

cerr << "\nError: " << e.what() << endl;

bSuccess = false;

}

 

if (hSocket!=INVALID_SOCKET)

{

closesocket(hSocket);

}

return bSuccess;

}

В качестве параметра в функцию передается имя сервера, к которому надо присоединиться. Так же в функции есть несколько, необходимых нам, параметров: дескриптор socket’а, временный буфер для хранения полученных данных и структура sockaddr_in для хранения адреса сервера. Дескриптор socket’а инициализируется значением INVALID_SOCKET, единственным значением, которое не может использоваться в качестве дескриптора socket’а. bSuccess переменная, которая принимает значение false, если функция завершилась неудачно. Основной код заключен в блоке try-catch, любая возникающая ошибка обрабатывается этой функцией. Задачи этой функции: -получение IP сервера, зная его имя -создание socket’а -подключение socket’а к серверу -отправить HTTP-запрос -получить данные и выводить их на экран, до тех пор, пока сервер не закроет соединение -закрыть socket Далее я покажу, как осуществить каждую из задач, а заодно и чем заполнить блок try-catch.

IP из имени

Для подключения к серверу нам необходимо заполнить структуру sockaddr_in адресом сервера. Для получения адреса сервера в нашей программе требуется создать функцию FindHostIP. Обратите внимание, что поиск сервера включает в себя запрос к DNS, поэтому этот процесс может занять некоторое время (обычно около 10 миллисекунд).Т.к. мы используем блокирующий socket, то наша программа будет просто висеть, пока выполняется эта функция. Хотя при использовании консольного приложения это не является большой проблемой.

IPNumber FindHostIP(const char *pServerName)

{

HOSTENT *pHostent;

 

// Заполняем структуру hostent:

if (!(pHostent = gethostbyname(pServerName)))

throw HeadReqException("could not resolve hostname.");

 

// Извлекаем IP адрес:

if (pHostent->h_addr_list && pHostent->h_addr_list[0])

return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);

 

return 0;

}

gethostbyname принимает на входе имя сервера и возвращает указатель на структуру hostent. Обратите внимание на то, что данная функция не может принимать на вход параметры, которые являются IP адресом (например "101.102.103.104"). Первый if проверяет может ли имя сервера быть преобразовано в ip адрес. Если нет, то функция возвращает NULL. Но если функция завершится успешно, то у нас будет заполненная структура hostent, из которой нам просто надо достать адрес сервера. Структура hostent может содержать список адресов, не все из них обязательно будут являться IP адресами. Т.к. мы используем TCP/IP, они будут IP адресами, но структура еще должна поддерживать другие формы адресов. Член структуры hostent h_addr_list указывает на массив других указателей. Каждый указатель в этом массиве указывает на адрес. Т.к. структура hostent не знает тип используемых адресов, мы должны преобразовать их в известный тип (в данном случае IPNumber*). FindHostIP извлекает первый доступный IP-адрес из этой структуры и возвращает его. Возвращаемое значение этой функции используется функцией FillSockAddr:

void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)

{

// Устанавливаем адресное семейство, номер порта и находим IP

pSockAddr->sin_family = AF_INET;

pSockAddr->sin_port = htons(portNumber);

pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);

}

Все что делает это функция, это вызывает FindHostIP и хранит IP в структуре sockaddr_in, на которую указывает параметр pSockAddr. Она также преобразовывает номер порта от типа portNumber к порядку байтов и сохраняет это в той же структуре. Возвращаясь к функции RequestHeaders, мы вызываем FillSockAddr, что бы заполнить нашу структуру sockaddr_in правильной информацией:

cout << "Looking up hostname " << pServername << "... ";

FillSockAddr(&sockAddr, pServername, SERVER_PORT);

cout << "found.\n";

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]