Клиентские функции
В разд. 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 — указанный адрес не может использоваться с данным сокетом. Эта ошибка возникает, когда указывается адрес в формате одного протокола, а производится попытка соединения по другому протоколу.