Программирование на C++ глазами хакера

         

Клиентские функции


В разд. 4.6.4 я познакомил вас с серверными функциями, и вы уже в состоянии написать сервер с помощью WinAPI-функций. Но мы пока не будем этого делать, потому что еще не можем написать клиентскую часть и протестировать пример. Так что теперь самое время заняться клиентскими функциями и функциями приема/передачи данных.

Для соединения с сервером нужны два этапа — создать сокет и подключиться к нему. Но это в идеальном случае. Как правило, добавляется еще один этап. Какой? Простым пользователям тяжело работать с IP-адресами, поэтому чаще всего они используют символьные имена серверов. В этом случае необходимо перед соединением с сервером выполнить еще одно действие — перевести символьное имя в IP-адрес.

Как создавать сокет, вы уже знаете. Теперь давайте разберемся с процессом определения IP-адреса. Для этого используется одна из двух функций: gethostbyname или WSAAsyncGetHostByName (в зависимости от версии WinSock). Для начала рассмотрим функцию gethostbyname:

struct hostent FAR * gethostbyname ( const char FAR * name );

В качестве единственного параметра нужно передать символьное имя сервера. Функция возвращает структуру типа hostent, которую рассмотрим чуть позже.

Теперь переходим к рассмотрению WSAAsyncGetHostByName:

HANDLE WSAAsyncGetHostByName ( HWND hWnd, unsigned int wMsg, const char FAR * name, char FAR * buf, int buflen );

Функция выполняется асинхронно, а это значит, что не блокируется выполнение программы при ее вызове. Программа будет работать дальше, но результат будет получен позже через сообщение Windows, указанное в качестве второго параметра. Это очень удобно, потому что процесс определения адреса может оказаться очень долгим, и блокирование программы на длительное время будет неэффективным. Процессорное время можно использовать для других целей.

Давайте разберем параметры подробнее:

hWnd — дескриптор окна, которому будет послано сообщение по завершении выполнения асинхронного запроса;

wMsg — сообщение Windows, которое будет сгенерировано после определения IP-адреса;


name — символьное имя компьютера, адрес которого надо определить;

buf — буфер, в который будет помещена структура hostent. Буфер должен иметь достаточный объем памяти. Максимальный размер можно определить с помощью макроса MAXGETHOSTSTRUCT;

buflen — длина буфера, указанного в четвертом параметре.

Теперь рассмотрим структуру hostent, с помощью которой получен результат:



struct hostent { char FAR * h_name; char FAR * FAR * h_aliases; short h_addrtype; short h_length; char FAR * FAR * h_addr_list; };

Проанализируем параметры структуры:

h_name — полное имя компьютера. Если в сети используется доменная система, то этот параметр будет содержать полное доменное имя;

h_aliases — дополнительное имя узла;

h_addrtype — тип возвращаемого адреса;

h_length — длина каждого адреса в списке адресов;

h_addr_list — список адресов компьютера.

Компьютер может иметь несколько адресов, поэтому структура возвращает полный список, который заканчивается нулем. В большинстве случаев достаточно выбрать первый адрес из списка. Если функция gethostbyname определила несколько адресов, то чаще всего по любому из них можно будет соединиться с искомым компьютером.

Теперь непосредственно функция соединения с сервером connect. Она выглядит следующим образом :

int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen );

Параметры функции:

s — предварительно созданный сокет;

name — структура SOCKADDR, содержащая адрес сервера, к которому надо подключиться;

namelen — размер структуры SOCKADDR, указанной в качестве второго параметра.

Во второй версии WinSock появилась функция WSAConnect:

int WSAConnect ( SOCKET s, const struct sockaddr FAR * name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS );

Первые три параметра ничем не отличаются от параметров функции connect. Поэтому рассмотрим только новые:

lpCallerData — указатель на пользовательские данные, которые будут отправлены серверу во время установки соединения;

lpCalleeData — указатель на буфер, в который будут помещены переданные во время соединения данные.



Оба параметра имеют тип указателя на структуру WSABUF, которая выглядит следующим образом:

typedef struct _WSABUF { u_long len; char FAR * buf; } WSABUF, FAR * LPWSABUF;

Здесь первый параметр — размер буфера, а второй — указатель на сам буфер. Последние два параметра функции WSAConnect (lpSQOS и lpGQOS) являются указателями на структуры типа QoS. Они определяют требования к пропускной способности канала при приеме и передаче данных. Если указать нулевое значение, то это будет означать, что требования к качеству обслуживания не предъявляются.

Во время попытки соединения чаще всего могут встретиться следующие ошибки:

WSAETIMEDOUT — сервер недоступен. Возможна какая-то проблема на пути соединения;

WSAECONNREFUSED — на сервере не запущено прослушивание указанного порта;

WSAEADDRINUSE — указанный адрес уже используется;

WSAEAFNOSUPPORT — указанный адрес не может использоваться с данным сокетом. Эта ошибка возникает, когда указывается адрес в формате одного протокола, а производится попытка соединения по другому протоколу.


Содержание раздела