Главная страница

Программирование в сетях Windows. Э. Джонс, Д. Оланд


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница16 из 50
1   ...   12   13   14   15   16   17   18   19   ...   50
Сигнальный протокол NNI позволяет двум коммутаторам обменивать- ся информацией о маршрутизации и управляющей информацией.
Здесь описаны только те элементы сигнального протокола UNI, которые касаются установки соединения ATM средствами Winsock. В настоящее вре- мя Winsock в Windows 2000 и Windows 98 (SP 1) поддерживает сигнальный протокол UNI версии 3.1.

150 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Для обмена данными по сети ATM с помощью сигнального протокола
UNI, Winsock использует механизм точек доступа к службам (Service Access
Point, SAP). Напомним, что для обмена данными через сеть ATM между ком- пьютерами должно быть предварительно установлено виртуальное соедине- ние. SAP позволяет приложениям Winsock зарегистрировать и идентифици- ровать интерфейс сокета для связи по сети ATM с помощью адресной струк- туры SOCKADDR_ATM. Создав SAP, Winsock устанавливает виртуальное соеди- нение между клиентом и сервером (используя сигнальный протокол UNI).
Структура SOCKADDR_ATM определена так:
typedef struct sockaddr_atm u_short
ATM.
ATM.
ATM.
ckac
.ADDRESS
.BLLI
.BHLI
idr atm,
satm_family;
satm.
satm.
satm.
.number;
.blli;
.bhli;
SOCKADDR ATM, «PSOCKADDR ATM, •LPSOCKADDR ATM;
Поле satmjamily всегда должно быть равноAF_ATM. Поле satmjiumber —
фактический адрес ATM в виде структуры ATM_ADDRESS. Он основан на од- ной из двух основных схем адресации ATM: E.I64, либо точках доступа к
сетевым службам (Network Service Access Point, NSAP). Адреса NSAP также на- зывают системными адресами ATM в стиле NSAP (NSAP-style ATM Endsystem
Address, AESA). Структура SOCKADDR_ATM определена так:
typedef struct
{
DWORD AddressType;
DWORD NumofDigits;
UCHAR Addr[ATM_ADDR_SIZE];
} ATM.ADDRESS;
В поле AddressType задается схема адресации: для ЕЛ64 значение поля должно быть АТМ_Е1б4, для NSAP — ATM_NSAP. Кроме того, если приложе- ние пытается осуществить привязку сокета к SAP, то этому полю можно при- своить и другие значения:
К АТМЕ164 — адрес ЕЛ 64, применяется при соединении с SAP;
Ш ATMNSAP — адрес в стиле NSAP, применяется при соединении с SAP;
Ш SAPFIELDANYAESASEL — адрес в стиле NSAP с параметризованным селекторным октетом, служит для привязки сокета к SAP;
SAPFIELDANYAESAREST — адрес в стиле NSAP со всеми октетами,
кроме параметризованного, служит для привязки сокета к SAP.
Поле NumofDigits всегда должно быть равно ATM_ADDR SIZE. В поле. Addr
содержится фактический 20-байтный адрес ATM по схеме ЕЛ 64 или NSAR
Поля satm_blli и satmbhli структуры SOCKADDR_ATM в ATM UNI пред- ставляют широкополосную информацию нижнего (Broadband Lower Layer
Information, BLLI) и верхнего уровня (Broadband Higher Layer Information,

Г Л А В А 6 Семейства адресов и разрешение имен
(
151
BHLI) соответственно. Вообще, эти структуры используются для идентифи- кации стека протокола, работающего поверх соединения ATM. В документах по ATM Form/IETF описано несколько стандартных комбинаций значений
BHLI и BLLI. (Одна комбинация идентифицирует соединение по ATM с эму- ляцией ЛВС, другая — IP поверх ATM и т. п.) Полные диапазоны значений для полей этих структур приведены в книге стандартов ATM UNI 3.1, а докумен- ты по ATM Form/IETF можно найти по адресу http://www.ietf.org.
Структуры данных BHLI и BLLI определены так:
typedef struct
{
DWORD HighLayerlnfoType;
DWORD HighLayerlnfoLength;
UCHAR HighLayerInfo[8];
} ATM_BHLI;
typedef struct
{
DWORD Layer2Protocol;
DWORD Layer2UserSpecifiedProtocol;
DWORD Layer3Protocol;
DWORD Layer3UserSpecifiedProtocol;
DWORD Layer3IPI;
UCHAR SnapID[5];
} ATMJ3LLI;
Подробности определения и использования этих полей выходят за рамки нашей книги. Для связи по сети ATM средствами Winsock приложению необ- ходимо присвоить значение SAP_FIELD_ABSENT следующим полям структур
BHLI и BLLI:
Ш ATMВШ Layer2Protocol;
Ш АТМВШ Layer3Protocol;
ATM BH11 HighLayerlnfoType.
Когда эти поля получают значение SAP_FIELD_ABSENT, другие поля обеих структур не используются. Вот пример, иллюстрирующий возможное при- менение структуры SOCKADDR_ATM для настройки SAP под адрес NSAP:
SOCKADDR.ATM atm_addr;
UCHAR MyAddress[ATM_ADDR_SIZE];
atm_addr.satm_family = AF_ATM;
atm_addr.satm_number.AddressType = ATM_NSAP;
atm
-addr.satm_number.NumofDigits = ATM_ADDR_SIZE;
a
tm_addr.satiti_blli.Layer2Protocol = SAP_FIELD_ABSENT;
a
tm_addr.satm_blli.Layer3Protocol = SAP_FIEID_ABSENT;
tm
-addr.satm_bhli.HighLayerlnfoType = SAP_FIELD_ABSENT;
mem
cpy(&atm_addr.satm_number.Addr, MyAddress, ATM_ADDR_SIZE);

1 52 ЧАСТЬ II Интерфейс прикладного программирования Winsock
ATM-адрес, как правило, представляет собой шестнадцатеричную ASCII- строку из 40 символов, которая соответствует 20 байтам, составляющим ад- рес в стиле NSAP или Е 164 в структуре ATM ADDRESS Например, адрес ATM
в стиле NSAP мог бы выглядеть так
47000580FFE1000000F21A1D540000D10FED5800
Преобразование этой строки в 20-байтный адрес может занять доволь- но много времени, однако Winsock предоставляет независимую от протоко- ла функцию — WSAStrtngToAddress, позволяющую преобразовывать 40-сим- вольную шестнадцатеричную строку ASCII в структуру ATMADDRESS (Эта функция подробно описана в конце главы ) Другой способ преобразовать шестнадцатеричную строку ASCII в шестнадцатеричный (двоичный) фор- мат — использовать функцию AtoH (листинг 6-2) Эта функция не входит в
Winsock, но разработать ее просто (см примеры в главе 7)
Листинг 6-2. Функции, преобразующие шестнадцатеричные строки ATM
//
// Функция AtoH
//
// Описание Эта функция преобразует ATM-адрес из строкового
// формата(ASCII) в двоичный (шестнадцатеричный)
//
void AtoH(CHAR *szDest, CHAR «szSource, INT iCount)
{
while (iCount-)
{
*szDest++ = ( BtoH ( *szSource++ ) « 4 )
+ BtoH ( *szSource++ );
}
return;
>
//
// Функция. BtoH
//
// Описание: Эта функция возвращает эквивалентное двоичное значение
// для отдельного символа в формате ASCII
//
UCHAR BtoH( CHAR ch )
{
if ( ch >= '0' && ch <= '9 1
)
{
return ( ch - '0' );
}
if ( ch >= 'A
1
&& ch <= 'F
f
)
{
return ( ch - 'A' + OxA );
, u a» * *•'

. Г Л А В А 6 Семейства адресов и разрешение имен ' 153
Листинг 6-2. (продолжение)
i f (ch >= 'a' && ch <= T )
return ( ch - 'а
1
+ ОхА ),
// Неверные значения при указании адреса не принимаются
return -1;
Создание сокета
В ATM приложения могут создавать только ориентированные на соединение сокеты, поскольку ATM позволяет связываться только по VC Поэтому данные могут быть переданы или как поток байт, или как отдельное сообщение Для открытия АТМ-сокета вызовите функцию socket или WSASocket с семейством адресов AF_ATM и типом сокета SOCK_RAW, а полю протокола присвойте
ATMPROTO_AAL5-
s = socket(AF_ATM, S0CK_RAW, ATMPR0T0_AAL5),
s = WSASocket(AF_ATM, SOCK_RAW, ATMPR0T0_AAL5, NULL, 0, WSA_FLAG_OVERLAPPED),
По умолчанию при открытии сокета (как в примере) создается потоко- вый сокет ATM Windows также выводит сведения о поставщике ATM, способ- ном передавать данные в виде сообщений Чтобы его использовать, явно определите поставщик исходного ATM-протокола для функции WSASocket
при помощи структуры WSAPROTOCOLJNFO (см главу 5) Это необходимо,
так как в Winsock в запросах socket и WSASOCKET каждому поставщику ATM
соответствуют три элемента семейство адресов, тип сокета и протокол По умолчанию, Winsock возвращает стандартную запись протокола, соответ- ствующую всем трем атрибутам, в нашем случае — это поставщик для пото- ковой передачи данных Следующий пример показывает, как найти постав- щика ATM, передающего данные в виде сообщений, и открыть сокет
dwRet = WSAEnumProtocols(NULL, lpProtocolBuf, idwBufLen),
for ( i = 0 , i < dwRet, i++)
if ((lpProtocolBuf[i].iAddressFamily == AF.ATM) &&
dpProtocolBuf[i] iSocketType == S0CK_RAW) &&
dpProtocolBuf[i] iProtocol == ATMPR0T0_AAL5) &&
dpProtocolBuf[i] dwServiceFlagsi &
XP1JIESSAGE_0RIENTED))
s = WSASocket(FR0M_PR0T0C0L_INF0, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO, lpProtocolBuf[i], 0,
WSA_FLAG_OVERLAPPED);
у l>,.RHM5Rf«M

1 54 ЧАСТЬ II Интерфейс прикладного программирования Wmsock
Привязка сокета к SAP
Адреса ATM довольно сложны, так как в 20 байтах адреса содержится много информационных элементов. Из них внимания программистов Winsock зас- луживает лишь последний байт. Последний байт адреса в стиле NSAP или
Е.164 соответствует номеру селектора, который уникальным образом позво- ляет приложению распознать и определить SAP на конечной точке. Как мы уже говорили, Winsock использует SAP для связи по сети ATM.
Приложение-сервер должно зарегистрировать SAP на конечной точке и дождаться, пока приложение-клиент соединится по зарегистрированному
SAP. Для клиента это означает лишь настройку структуры SOCKADDR_ATM с типом адреса ATM_Е164 или ATM_NSAP и предоставление адреса ATM, связан- ного с SAP сервера. Для создания SAP и ожидания соединений приложение должно сначала создать сокет для семейства адресов AF_ATM. Затем необхо- димо определить структуру SOCKADDRATM, используя такие типы адреса, как
SAP_FIELD_ANY_AESA_SEL, SAP_FIELD_ANY_AESA_REST,ATM_El64 илиАТМ_№АР
(см. список значений поля AddressType в разделе «Протокол ATM Адреса- ция»). Для АТМ-сокета SAP создается при вызове приложением функции bind
(см. главу 7), а типы адреса определяют способ создания SAP на вашей ко- нечной точке.
Тип адреса SAPJFIELD_ANY_AESA_SEL позволяет создать SAP для прослуши- вания любого входящего соединения ATM (параметризация адреса ATM и селектора). Это значит, что только один сокет может быть привязан к этой конечной точке для прослушивания любых соединений: при попытке соеди- нения другого сокета с этим типом адреса выдается ошибка WSAEADDRINUSE.
Впрочем, вы можете явно сопоставить дополнительные сокеты конечной точ- ке для конкретного селектора. Применяя тип адреса SAP_FIELD_ANY_AESA_REST,
можно создать SAP, явно привязанную к указанному селектору конечной точ- ки (параметризация адреса ATM без селектора). Нельзя одновременно ис- пользовать более одного сокета, привязанного к конкретному селектору на конечной точке, иначе вызов bind вернет ошибку WSAEADDRINUSE. Для типа
SAP_FIELD_ANY_AESA_SEL в структуре ATM_ADDRESS задайте ATM-адрес из нулей, для типа SAP_FIELD_ANY_AESAJREST — обнулите первые 19 байт АТМ- адреса. Последний байт должен указывать номер требуемого селектора.
Сокеты, связанные с конкретными селекторами (SAP_FIELD_ANY_AESA_REST),
приоритетнее связанных с параметризованными селекторами (SAP_FIELD_
ANY_AESA_SEL). А потому первыми при соединениях будут выбраны или они,
или сокеты, связанные с явными интерфейсами — ATM_NSAP и АТМЕ164-
(Если у конечной точки запрошено входящее соединение с указанием явно слушаемого селектора, сокет принимает соединение.) Подключение к соке- ту с указанием параметризованного селектора произойдет только в отсут- ствие доступных сокетов, связанных с конкретными селекторами. Создание сокета, прослушивающего соединения по SAP, описано в главе 7.
Наконец, служебная программа Atmadm.exe позволяет получить всю ин- формацию об ATM-адресе и виртуальном соединении на конечной точке.
Это может пригодиться при разработке приложения ATM, когда понадобится

Г Л А В А 6 Семейства адресов и разрешение имен i 155
информация о доступных на конечной точке интерфейсах. Вот параметры командной строки это программы:
щ -с — перечисляет все соединения (VC), удаленный адрес и локальный интерфейс;
ш
_
а
— перечисляет все зарегистрированные адреса (все локальные интер- фейсы ATM и их адреса);
щ _
s
— выводит статистику: текущее число запросов, количество принятых и отправленных сигнальных пакетов и ILMI-пакетов и т. п.
Разрешение имен
В настоящее время для ATM под Winsock нет доступных поставщиков имен.
Поэтому, к сожалению, требуется задавать 20-байтный адрес ATM для сокет- ной связи по сети ATM. В главе 10 рассматривается DNS-пространство Win- dows 2000, в котором можно регистрировать ATM-адреса с дружественны- ми именами служб.
Дополнительные функции Winsock 2
Функции WSAAddressToString и WSAStringToAddress в Winsock 2 обеспечивают независимый от протокола способ преобразования структуры SOCKADDR
протокола в форматированную строку символов и наоборот. Так как эти функции не зависят от протокола, транспортный протокол должен поддер- живать преобразование строк. В настоящее время эти функции работают только для семейств адресов AFJNET и AF_ATM. Функция WSAAddressToString
определена так:
INT WSAAddressToString(
LPSOCKADDR lpsaAddress,
DWORD dwAddressLength,
LPWSAPROTOCOL_INFO lpProtocolInfo,
OUT LPTSTR lpszAddressString,
IN OUT LPDWORD lpdwAddressStringLength
);
Параметр lpsaAddress соответствует структуре SOCKADDR для конкретного протокола, содержащего адрес, который надо преобразовать в строку. Пара- метр dwAddressLength задает размер структуры первого параметра для каж- дого протокола. Необязательный параметр lpProtocolInfo представляет по- ставщик протокола. Поставщиков протокола можно найти функцией WSAE-
numProtocols (см. главу 5). Если вы зададите NULL, вызов использует постав- щик первого протокола, поддерживающего семейство адресов из lpsaAddress.
Параметр lpszAddressString — буфер, где сохраняется удобная для чтения ад- ресная строка. Параметр lpdwAddressStringLength — это размер ipszAddres-
s
tring. При выводе в нем возвращается длина строки, фактически скопиро- ванной в lpszAddressString. Если предоставленный буфер мал, функция выда- е т о щ и б к у WSAEFAULT, а в параметре lpdwAddressStringLength вернется требу- емый размер в байтах.

1 5 6 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Функция WSAStringToAddress, напротив, преобразует удобную для чтения адресную строку в структуру SOCKADDR:
INT WSAStringToAddress(
LPTSTR AddressString,
INT AddressFamily,
LPWSAPR0T0C0L_INF0 lpProtocolInfo,
LPSOCKADDR lpAddress,
LPINT lpAddressLength
);
В параметре AddressString передается адресная строка, формат которой для поддерживаемых протоколов:
Я для IP — XXXXXXXXXXXXY, где X представляет октет в строке IP-адре- са, а У — номер порта;
Ш для ATM - NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN,
где 40 символов N представляют 20-байтный ATM-адрес в шестнадцате- ричном формате.
Параметр AddressFamily представляет тип семейства адресов для парамет- ра AddressString. Необязательный параметр lpProtocolInfo представляет по- ставщик протокола. Если вы присвоите ему NULL, Winsock будет искать пер- вый доступный поставщик протокола для типа адресного семейства из Add-
ressFamily. Для выбора конкретного поставщика получите с помощью WSA-
EnumProtocols список доступных поставщиков протокола, установленных на вашей системе. В параметре lpAddress хранится структура SOCKADDR, прини- мающая информацию в адресной строке. Параметр lpAddressLength представ- ляет размер результирующей структуры SOCKADDR.
Резюме
Мы описали семейства адресов протоколов, поддерживаемых Winsock, и пояснили специфичные атрибуты адресации. Для каждого семейства адре- сов были подробно рассмотрены создание сокета и настройка структуры адреса сокета для организации связи по протоколу.
В следующей главе обсуждаются основные методики связи в Winsock,
применимые ко всем семействам адресов, рассмотренным в этой главе.
XI.

Г Л А В А
Основы Winsock
Эта глава посвящена изучению основных методик и API-вызовов, необходи- мых для написания сетевых приложений. Из материалов предыдущей главы вы знаете, как протоколы, доступные из Winsock, адресуют компьютеры и службы. Здесь мы рассмотрим способы установления соединения между дву- мя компьютерами в сети и механизмы обмена данными. Во избежание по- второв, мы обсудим лишь протокол TCP/IP. На прилагаемом компакт-диске содержатся примеры клиент-серверных приложений для каждого из рас- смотренных в главе б протоколов. Единственная зависящая от протокола операция — это создание сокета. Большинство остальных вызовов функций
Winsock, ответственных за установление соединения, отправку и прием дан- ных, не зависят от протокола. Все исключения упоминались в главе 6 при обсуждении конкретных протоколов.
Представленные в этой главе примеры помогут вам лучше понять вызо- вы Winsock, необходимые для установления соединений и обмена данными.
Наша цель — изучить эти вызовы, поэтому в примерах используются прямые блокирующие вызовы Winsock. Другие модели ввода-вывода, реализованные в Winsock, обсуждаются в главе 8.
Кроме того, будут представлены разновидности API-функций для версий
Winsock 1 и 2. Если в спецификации Winsock 2 обновлена или добавлена новая API-функция, то ее имя начинается с префикса WSA. Например, имя функции Winsock 1, создающей сокет — socket. В Winsock 2 есть ее новая версия — WASSocket, использующая расширенные возможности Winsock 2.
Исключения из этого правила: функции WSAStartup, WSACleanup, WSARecvEx
и WSAGetLastError, — упоминаются уже в спецификации Winsock 1.1.
Инициализация Winsock
Любое Winsock-приложение перед вызовом функции должно загрузить со- ответствующую версию библиотеки Winsock. Если этого не сделать, функция вернет значение SOCKET_ERROR и выдаст ошибку WSANOTINITIAUSED. Загруз- ку библиотеки Winsock выполняет функция WSAStartup:
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData

158 ЧАСТЬ II Интерфейс прикладного программирования Wmsock
Параметр wVersionRequested задает версию загружаемой библиотеки Win- sock. Старший и младший байты определяют дополнительный и основной номер версии библиотеки соответственно. Для получения значения парамет- ра wVersionRequested можно использовать макрос MAKEWORD(x, у), где х
старший байт, а у — младший.
Параметр ipWSAData — указатель на структуру LPWSADATA, которая при вызове функции WSAStartup заполняется сведениями о версии загружаемой библиотеки:
typedef struct WSAData
WORD
WORD
char
char
«version;
wHighVersion;
szDescription[WSADESCRIPTION_LEN
szSystemStatus[WSASYS_STATUS_LEN
+ 1];
+ 1];
unsigned short IMaxSockets;
unsigned short iMaxUdpDg;
char FAR * lpVendorlnfo;
} WSADATA, FAR * LPWSADATA;
WSAStartup присваивает параметру wVersion значение загружаемой версии.
Параметр wHighVersion содержит номер последней доступной версии Winsock.
Помните, что в обоих полях старший байт определяет дополнительный, а младший — основной номер версии. Поля szDescription и szSystemStatus запол- няются не во всех реализациях Winsock и практически не применяются.
Не используйте и поля iMaxSockets и iMaxUdpDg. Предполагается, что в них заданы максимальное количество одновременно открытых сокетов и максимальный размер дейтаграммы. Для определения последнего следует запросить сведения о протоколе, вызвав функцию WSAEnumProtocols. Макси- мальное количество одновременно открытых сокетов зависит от свободной физической памяти. Наконец, поле lpVendorlnfo зарезервировано для инфор- мации изготовителя реализации Winsock и не используется ни на одной из платформ Win32.
Разные платформы Windows поддерживают следующие версии Winsock:
Windows 9 5 — 1 . 1 (2.2); Windows 98, NT 4.0, 2000 — 2.2; Windows СЕ — 1.1.
Важно различать основные версии библиотеки. Winsock 1.x не поддержи- вает многие расширенные возможности Winsock, описанные в этом разде- ле. К тому же, для использования в приложении Winsock 1 необходимо под- ключить файл Winsock.h, а для Winsock 2 — Winsock2.h.
ПРИМЕЧАНИЕ Обновление Winsock 2 для Windows 95 можно найти по адресу http://www.microsoft.com/windows95/downloads/.
Даже если платформа поддерживает Winsock 2, не обязательно использовать самую последнюю версию. Напротив, если необходимо, чтобы приложение поддерживалось несколькими платформами, возьмите за основу Winsock 1-1-
Такое приложение будет отлично работать на платформе Windows NT 4.0, по- тому что все вызовы Winsock 1.1 имеются в Winsock 2 DLL.

Г Л А В А 7 Основы Winsock , 159
Как правило, если выходит новая версия Winsock, разработчики старают- ся ее обновить. В новых версиях исправлены ошибки, к тому же старый код должен без проблем выполняться, по крайней мере, теоретически. В неко- торых случаях поведение Winsock отличается от определенного специфи- кацией. В итоге многие программисты пишут приложения с учетом работы
Winsock на конкретной платформе, а не согласно спецификации.
Например, в Windows NT 4.0 при использовании программой модели асин- хронных оконных событий после каждого успешного выполнения функции
send или WSASend асинхронно выдается сообщение FDWRITE, что указывает на возможность записи данных. Однако в спецификации говорится, что со- бытие FDJWR1TE выдается, когда приложение готово отправлять данные (на- пример, сразу после запуска), и что FD_WRITE означает: следует продолжать запись, пока не будет выдана ошибка WSAEWOULDBLOCK. В действительнос- ти, после того как система отправит все ожидающие обработки данные и при- готовится обрабатывать очередные вызовы send и WSASend, она отправит окну приложения событие FDJWRITE-. значит, в этот момент вы можете возобновить запись данных в сеть (статья Q186245 в базе знаний). Эта проблема устране- на в четвертом пакете обновлений для Windows NT 4.0 и 2000.
В большинстве случаев при написании новых приложений следует загру- жать последнюю доступную версию библиотеки Winsock. Если будет выпуще- на версия 3, приложение, использующее версию 2.2, должно выполняться кор- ректно. При запросе более поздней версии Winsock, не поддерживаемой ва- шей платформой, WSAStartup вернет ошибку, а в поле wHighVersion структуры
WSADATA появится номер последней версии библиотеки, поддерживаемой данной системой.
Проверка и обработка ошибок
Проверка и обработка ошибок играют весомую роль при написании Win- sock-приложения. Функции Winsock достаточно часто возвращают ошибки,
но как правило, не критические — передачу информации можно продол- жать. Большинство функций Winsock при ошибке вызова возвращают зна- чение SOCKET_ERROR, но так происходит не всегда. При подробном рассмот- рении API-вызовов мы обратим внимание на возвращаемые значения, соот- ветствующие ошибкам. Константа SOCKET'J5RROR на самом деле равна - 1 .
Для получения более информативного кода ошибки, возникшей после одно- го из вызовов Winsock, задействуйте функцию WSAGetLastError.
int WSAGetLastError (void);
Эта функция возвращает код последней ошибки. Всем кодам ошибок, воз- вращаемым WSAGetLastError, соответствуют стандартные константные значе- ния. Они описаны в Winsockh или в Winsock2.h (в зависимости от версии insock). Единственное различие этих заголовочных файлов — Winsock2.h с о
Держит больше кодов ошибок новых API-функций. Константы, определенные
Для кодов ошибок директивой *define, обычно начинаются с префикса WSAE.

160
ЧАСТЬ II Интерфейс прикладного программирования Winsock
Протоколы с установлением соединения
Сначала мы рассмотрим функции Winsock, необходимые для приема и ус- тановления соединений: обсудим, как слушать соединения клиентов, и изу- чим процесс принятия или отклонения соединения Затем поговорим о том,
как инициировать соединение с сервером. В заключение будет описан про- цесс передачи данных в ходе сеанса связи.
Серверные API-функции
Сервер — это процесс, который ожидает подключения клиентов для обслу- живания их запросов. Сервер должен прослушивать соединения на стандар- тном имени. В TCP/IP таким именем является IP-адрес локального интерфей- са и номер порта. У каждого протокола своя схема адресации, а потому и свои особенности именования. Первый шаг установления соединения —
привязка сокета данного протокола к его стандартному имени функцией
bind. Второй — перевод сокета в режим прослушивания функцией listen. И
наконец, сервер должен принять соединение клиента функцией accept или
WSAAccept.
Рассмотрим каждый API-вызов, необходимый для привязки, прослушива- ния и установления соединения с клиентом. Базовые вызовы, которые кли- ент и сервер должны сделать для установления канала связи, иллюстрирует рис. 7-1.
Сервер Winsock
Клиент Winsock
sacket/WSASocket
i d И
С i
emoeet/WSACotmect
Рис. 7-1. Основные этапы работы клиента и сервера Winsock
Функция bind
После создания сокета определенного протокола следует связать его со стан- дартным адресом, вызвав функцию bind:
int bind(
SOCKET s,
const struct sockaddr FAR* name,
int ' namelen

Г Л А В А 7 Основы Wmsock (161
Параметр s задает сокет, на котором вы ожидаете соединения клиентов.
Второй параметр с типом struct sockaddr — просто универсальный буфер,
фактически, в этот буфер вы должны поместить адрес, соответствующий стандартам используемого протокола, а затем при вызове bind привести его к типу struct sockaddr. В заголовочном файле Winsock определен тип SOCK-
ADDR, соответствующий структуре struct sockaddr. Далее в главе этот тип бу- дет использоваться для краткости Последний параметр задает размер пере- данной структуры адреса, зависящей от протокола. Например, следующий код иллюстрирует привязку при ТСР-соединении:
SOCKET s;
struct sockaddr_in tcpaddr;
i n t port = 5150;
s = socket(AF_INET, SOCK.STREAM, IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(s, (SOCKADDR *)&tcpaddr, sizeof(tcpaddr));
Подробнее о структуре sockaddrjn — в разделе, посвященном адресации
TCP/IP, главы 6. Там приведен пример создания потокового сокета и после- дующей настройки структуры адреса TCP/IP для приема соединений клиен- тов. В данном случае сокет указывает на IP-интерфейс по умолчанию с но- мером порта 5150. Формально вызов blind связывает сокет с IP-интерфейсом и портом.
При возникновении ошибки функция bind возвращает значение SOCK-
ET _ERROR. Самая распространенная ошибка при вызове bind WSAEAD-
DRINUSE. В случае использования TCP/IP это означает, что с локальным IP- интерфейсом и номером порта уже связан другой процесс, или они нахо- дятся в состоянии TIMEJVA1T. При повторном вызове bind дня уже связанного сокета возвращается ошибка WSAEFAULT.
Функция listen
Теперь нужно перевести сокет в режим прослушивания. Функция bind толь- ко ассоциирует сокет с заданным адресом. Для перевода сокета в состояние ожидания входящих соединений используется API-функция listen-.
int listen(
SOCKET s,
int backlog
Первый параметр — связанный сокет. Параметр backlog определяет мак- симальную длину очереди соединений, ожидающих обработки, что важно
Ри запросе нескольких соединений сервера. Пусть значение этого парамет-
Равно 2, тогда при одновременном приеме трех клиентских запросов

1 62 ЧАСТЬ II Интерфейс прикладного программирования Winsock первые два соединения будут помещены в очередь ожидания, и приложение сможет их обработать Третий запрос вернет ошибку WSAECONNREFUSED
После того как сервер примет соединение, запрос удаляется из очереди, а другой — занимает его место Значение backlog зависит от поставщика про- токола Недопустимое значение заменяется ближайшим разрешенным Стан- дартного способа получить действительное значение backlog нет
Ошибки, связанные с listen, довольно просты Самая частая из них —
WSAEINVAL, обычно означает, что перед listen не была вызвана функция bind
Иногда при вызове listen возникает ошибка WSAEADDRINUSE, но чаще она происходит при вызове bind
Функции accept и WSAAccept
Итак, все готово к приему соединений клиентов Теперь вызовем функцию
accept или WSAAccept Прототип accept
SOCKET accept(
SOCKET s,
struct sockaddr FAR* addr,
int FAR* addrlen
),
Параметр s — связанный сокет в состоянии прослушивания Второй па- раметр — адрес действительной структуры SOCKADDRJN, a addrlen — ссыл- ка на длину структуры SOCKADDRJN Для сокета другого протокола замените
SOCKADDR_IN на структуру SOCKADDR, соответствующую этому протоколу
Вызов accept обслуживает первый находящийся в очереди запрос на соеди- нение По его завершении структура addr будет содержать сведения об IP- адресе клиента, отправившего запрос, а параметр addrlen — размер струк- туры
Кроме того, accept возвращает новый дескриптор сокета, соответствую- щий принятому клиентскому соединению Для всех последующих операций с этим клиентом должен применяться новый сокет Исходный прослуши- вающий сокет используется для приема других клиентских соединений и продолжает находиться в режиме прослушивания
В Winsock 2 есть функция WSAAccept, способная устанавливать соедине- ния в зависимости от результата вычисления условия
SOCKET WSAAccept(
SOCKET s,
struct sockaddr FAR * addr,
LPINT addrlen,
LPCONDITIONPROC lpfnCondition,
DWORD dwCallbackData
),
Первые три параметра — те же, что и в accept для Winsock 1 Параметр
lpfnCondition — указатель на функцию, вызываемую при запросе клиента
Она определяет возможность приема соединения и имеет следующий про- тотип

' '•"' - эч-ммвф<чтг> -чп» Г Л А В А 7 Основы Winsock 163
\
\
i n t CALLBACK ConditionFunc(
LPWSABUF l p C a l l e r l d ,
LPWSABUF lpCallerData,
(
LPQOS lpSQOS,
, LPQOS lpGQOS,
LPWSABUF lpCalleeld,
LPWSABUF lpCalleeData,
'
f
GROUP FAR * g,
DWORD dwCallbackData
),
Передаваемый по значению параметр lpCallerld содержит адрес соеди- няющегося объекта Структура WSABUF используется многими функциями
Winsock 2 и определена так typedef struct „WSABUF {
u_long len,
char FAR • buf,
} WSABUF, FAR . LPWSABUF,
В зависимости от ее использования, поле len определяет размер буфера,
на который ссылается поле buf или количество данных в буфере buf
Для lpCallerld параметр buf указывает на структуру адреса протокола, по которому осуществляется соединение Чтобы получить корректный доступ к информации, просто приведите указатель buf к соответствующему типу
SOCKADDR При использовании протокола TCP/IP это должна быть структу- ра SOCKADDR_IN, содержащая IP-адрес подключающегося клиента Большин- ство сетевых протоколов удаленного доступа поддерживают идентифика- цию абонента на этапе запроса
Параметр lpCallerData содержит данные, отправленные клиентом в ходе запроса соединения Если эти данные не указаны, он равен NULL Имейте в виду, что большинство сетевых протоколов, таких как TCP/IP, не использу- ют данные о соединении Чтобы узнать, поддерживает ли протокол эту воз- можность, обратитесь к соответствующей записи в каталоге Winsock путем вызова функции WSAEnumProtocols (см также главу 5)
Следующие два параметра — lpSQOS и lpGQOS, задают уровень качества обслуживания, запрашиваемый клиентом Оба параметра ссылаются на струк- туру, содержащую сведения о требованиях пропускной способности для приема и передачи Если клиент не запрашивает параметры качества обслу-
живания (quality of service, QoS), то они равны NULL Разница между ними в
Том, что ipSQoS используется для единственного соединения, a lpGQOS — для групп сокетов Группы сокетов не реализованы и не поддерживаются в Wins- ock 1 и 2 (Подробнее о QoS — в главе 12 )
Параметр lpCalleeld — другая структура WSABUF, содержащая локальный а
Дрес, к которому подключен клиент Снова поле buf указывает на объект
SOCKADDR соответствующего семейства адресов Эта информация полезна,
е<
-ли сервер запущен на многоадресной машине Помните, что если сервер связан с адресом 1NADDR_ANY, запросы соединения будут обслуживаться на
Любом сетевом интерфейсе, а параметр — содержать адрес интерфейса, ПРИ-
НЯВШЕГО ГПРЛ1то,„„

1 64 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Параметр ipCalleeData дополняет ipCallerData Он ссылается на структу ру WSABUF, которую сервер может использовать для отправки данных кли- енту в ходе установления соединения Если поставщик услуг поддерживает эту возможность, поле len указывает максимальное число отправляемых байт В этом случае сервер копирует некоторое, не превышающее это зна- чение, количество байт, в блок buf структуры WSABUF и обновляет поле len,
чтобы показать, сколько байт передается Если сервер не должен возвращать данные о соединении, то перед возвращением условная функция приема соединения присвоит полю len значение 0 Если поставщик не поддержи- вает передачу данных о соединении, поле len будет равно 0 Опять же, боль- шинство протоколов фактически, все, поддерживаемые платформами Win32
— не поддерживают обмен данными при установлении соединения
Обработав переданные в условную функцию параметры, сервер должен решить принимать, отклонять или задержать запрос соединения Если со- единение принимается, условная функция вернет значение CF_ACCEPT, если отклоняется — CFJREJECT Если по каким-либо причинам на данный момент решение не может быть принято, возвращается CF_DEFER
Как только сервер готов обработать запрос, он вызывает функцию WSA-
Accept Заметьте, что условная функция выполняется в одном процессе с
WSAAccept и должна работать как можно быстрее В протоколах, поддержи- ваемых платформами Win32, клиентский запрос задерживается, пока не бу- дет вычислено значение условной функции В большинстве случаев базовый сетевой стек ко времени вызова условной функции уже может принять со- единение А при возвращении значения CFJREJECT стек просто закрывает его Сейчас мы не будем углубляться в детали использования условной функ- ции принятия соединения — см главу 12
При возникновении ошибки возвращается значение INVALID_SOCKET,
чаще всего — WSAEWOULDBLOCK Оно возникает, если сокет находится в асинхронном или неблокирующем режиме и нет соединения для приема
Если условная функция вернет CF_DEFER, WSAAccept генерирует ошибку
WSATRY_AGAIN, если CF_REJECT- WSAECONNREFUSED
API-функции клиента
Клиентская часть значительно проще и для установления соединения тре- буется всего три шага создать сокет функцией socket или WSASocket, разре- шить имя сервера (зависит от используемого протокола), инициировать соединение функцией connect или WSAConnect
Из материалов главы 6 вы уже знаете, как создать сокет и разрешить имя
IP-узла, так что единственным оставшимся шагом является установление соединения В главе 6 также рассматривались способы разрешения имен и для других семейств протоколов
Состояния TCP
Для работы с Winsock не обязательно знать о состояниях TCP, но с их помощью можно лучше понять, что происходит с протоколом при

1   ...   12   13   14   15   16   17   18   19   ...   50


написать администратору сайта