В предыдущей статье мы рассмотрели некоторые функции и способы работы с ними. Давайте оформим весь предыдущий материал в небольшую программу. Сразу хочу предупредить, что исходный код нашего "творения" будет иметь больше теоретическую, чем практическую ценность. И вот почему:

* мы скомпонуем все известные нам функции в вполне рабочий код;
* мы еще не знаем некоторых функций, которые сделали бы этот код еще более "рабочим".



Проще говоря, мы еще не настолько владеем предметом, что бы писать серьезные программы под Winsock, но нужно же с чего-то начать!!!

Итак, техническое задание: написать программу, которая получает с удалённого HTTP сервера нужную нам HTML страничку. Проще говоря, "достать" с нужного нам сайта, кусочек HTML кода, по заранее известному адресу. Например, взять HTML страничку index.html с сайта www.Shelek.com - значит выполнить обращение по адресу www.Shelek.com/index.html. То есть, мы пишем программу, отдалённо напоминающую интернет-браузер .

В качестве протокола обмена данными с сервером можно использовать HTTP, FTP и другие протоколы. Мы будем использовать HTTP протокол. В принципе, Winsock все равно, какой протокол вы используете для связи с сервером. Его задача - доставить данные серверу и получить ответ. В данной статье мы не будем останавливаться на особенностях протокола HTTP, и вам придется принять на веру все, что касается этого протокола в коде программы. В будущем мы рассмотрим и работу с HTTP! Всё что нам нужно знать для того, что бы установить соединение с каким-нибудь сервером, это его адрес и порт. Например, 207.46.225.60 - это адрес HTTP сервера компании Microsoft. Протокол HTTP использует порт 80. Протокол FTP - 21.Проблемма в том, что некоторые сервера имеют динамические адреса. В таком случае их адреса вычисляются с помощью специальных функций Winsock API.

Для того, что бы узнать IP адрес машины зная ёё имя, существует функция gethostbyname. Для получения имени машины по ёё адресу используем функцию gethostbyaddr. Рассмотрим эти функции подробнее.

Код:
struct hostent FAR *gethostbyname(const char FAR *name );

Пример вызова:
Код:
hostent* d_addr; // Структура, в которую будет помещен IP адрес, // при возврате.
hostent* hn = gethostbyname ("www.Shelek.com");
//...

Если функция отработала успешно, то структура hn содержит нужные нам данные, иначе gethostbyname возвращает NULL;

Рассмотрим структуру hn.
Код:
struct hostent {
char FAR * h_name; // Официальное имя машины
char FAR * FAR * h_aliases; // Массив альтернативных имен машины // (заканчивающийся 0)
short h_addrtype; // Тип адреса (AF_INET...)
short h_length; // Длина адреса в байтах
char FAR * FAR * h_addr_list; // Список адресов (заканчивающийся 0)
};

Итак, после вызова gethostbyname нас интересуют следующие поля этой структуры:

h_name - можем узнать официальное имя машины :)
h_addrtype - тип адреса (для TCP/IP должно быть AF_INET)
h_length - длинна адреса (для TCP/IP V4 - 4 байта)
h_addr_list - массив адресов (заканчивающийся 0).
Для получения первого адреса в массиве, можно использовать макрос h_addr_list[0].

Для того, что бы получить адрес интересующей нас машины, необходимо вызвать gethostbyname (имя машины) и проанализировав полученные назад данные, выбрать из массива h_addr_list ее адрес. Адрес машины сохраним в структуре sockaddr_in (см. первую статью).

Полный пример:
Код:
sockaddr_in adr;
// ...
hostent* d_addr = gethostbyname ("www.Shelek.com");
adr.sin_addr.S_un.S_addr = *(DWORD* ) hn-h_addr_list[0];
// ...

Функция gethostbyaddr получая на вход адрес, тип адреса и его длину, возвращает имя машины.
Код:
struct HOSTENT FAR * gethostbyaddr(
const char FAR *addr, // Адрес машины (в сетевом виде)
int len, // Длинна адреса
int type // Тип адреса
);

Пример:
Код:
DWORD a = inet_addr ("192.168.0.4"); // Адрес машины в сетевом формате
hn = gethostbyaddr ((char* )&a, 4, AF_INET);

Если функция выполнилась успешно, поле h_name структуры hn будет содержать имя машины.
Вернёмся к написанию программы.

Структура программы такова:
1. Узнаём IP адрес сервера.
2. Устанавливаем соединение.
3. Посылаем серверу запрос (в HTTP формате).
4. Получаем ответ и выводим его на экран
5. Закрываем соединение и завершаем работу.

Приступим, но сразу хочу оговорить одну важную деталь: данный код будет работать только на машинах имеющих "прямой" выход и интернет, то есть этот код не буде работать, если вы сидите за прокси-сервером и т.п. Как обойти эти ограничения мы обязательно рассмотрим в следующих выпусках.
Вот исходный код программы (пример на VC++ 6.0 можно скачать отсюда .
Код:
#include "stdafx.h"
#include "winsock2.h"

#define request "GET /index.html HTTP/1.0 \r\nHost: www.Shelek.com\r\n\r\n" // HTML запрос.
#define MAX_PACKET_SIZE 4096

int main(int argc, char* argv[])
{
WSADATA ws;
SOCKET s;
sockaddr_in adr;
hostent* hn;
char buff [MAX_PACKET_SIZE];
// Init
if (WSAStartup (0x0101, &ws) != 0)
{
// Error
return -1;
}
// Создаём сокет
if (INVALID_SOCKET == (s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ) )
{
// Error
return -1;
}

// Получаем адрес
if (NULL == ( hn = gethostbyname ("www.Shelek.com") ) )
{
// Error
return -1;
}

// Заполняем структуру с адресом
adr.sin_family = AF_INET;
adr.sin_addr.S_un.S_addr = *(DWORD* ) hn-h_addr_list[0];
adr.sin_port = htons (80);

// Устанавливаем соединение с сервером
if (SOCKET_ERROR == connect (s, (sockaddr* )&adr, sizeof (adr) ) )
{
// Error
return -1;
}

// Посылаем запрос
if (SOCKET_ERROR == send (s, &request, sizeof (request), 0) )
{
// Error
return -1;
}

// Получаем ответ
int len = recv (s, (char *) &buff, MAX_PACKET_SIZE, 0);

if ( (len == SOCKET_ERROR) || (len == 0) )
{
// Error
return -1;
}

// Выводим результат
for (int i = 0; i printf ("%c", buff [i]);

// Закрываем соединение
if (SOCKET_ERROR == closesocket (s) )
{
// Error
return -1;
}

// Всё! :)
return 1;
}

Попробуйте открыть ссылку http://www.shelek.com/index.html - в окне вашего браузера должна появится начальная страница сайта. Программа должна выдать HTML код страници, после системной информации сервера. Вы сами всё увидите, если программа заработает на вашей машине. Если нет - не отчаивайтесь, у Вас хотя бы есть рабочий пример!
Так как размер статьи превышает запланированный объём, то анонсированные в предыдущей статье разделы мы обсудим в следующем выпуске!
В следующем выпуске читайте:

* Решаем проблему "блокировки" сокетов. (И мы ёё решим-таки!!! :) )
* UDP сокеты - приём/доставка негарантированных пакетов
Information
  • Posted on 31.01.2010 21:52
  • Просмотры: 414