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

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


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница37 из 50
1   ...   33   34   35   36   37   38   39   40   ...   50

ГЛАВА 14 Интерфейс Winsock 2 SPI
443
LPWSAOVERLAPPED lpOverlapped,
DWORD dwError,
DWORD cbTransferred,
LPINT lpErrno
Параметры s и lpOverlapped представляют исходный сокет и структуру
WSAOVERLAPPED, которые были первоначально переданы клиентом. Ваш поставщик должен присвоить параметру dwError значение, характеризую- щее состояние завершения запроса перекрытого ввода-вывода, а параметру
cbTransferred — число переданных в ходе перекрытой операции байт. Пос- ледний параметр — lpErrno, сообщает код ошибки Winsock, если обращение к этой функции вернуло значение SOCKETERROR. Когда выполнение WPU-
CompleteOverlappedRequest завершается, она присваивает значения двум по- лям в структуре WSAOVERLAPPED SPI-клиента: полю InternalHigh — количе- ство переданных и принятых байт cbTransferred, а полю Internal — значение,
отличное от WSS_OPERATION_IN PROGRESS.
В основанном на событиях перекрытом вводе-выводе для восстановления результата завершенного перекрытого запроса SPI-клиент в конечном сче- те вызывает функцию WSPGetOverlappedResult:
BOOL WSPGetOverlappedResult (
SOCKET s,
LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer,
BOOL fWait,
LPDWORD lpdwFlags,
LPINT lpErrno
UK-
}
'f
При вызове этой функции ваш поставщик службы должен сообщить о текущем состоянии исходного перекрытого запроса. Как мы уже упомина- ли, ваш поставщик отвечает за управление полями Internal, InternalHigh, Offset
и OffsetHigh в структуре WSAOVERLAPPED SPI-клиента. Когда вызывается функ- ция WSPGetOverlappedResult, ваш поставщик должен сначала проверить поле
Internal структуры WSAOVERLAPPED SPI-клиента. Если это поле содержит зна- чение WSS_OPERATION_IN_ PROGRESS, поставщик все еще обрабатывает пе- рекрытый запрос. Если значение параметра fWait в WSPGetOverlappedResult
TRUE, поставщик должен ждать завершения перекрытой операции: переда- чи описателя события в структуру WSAOVERLAPPED SPI-клиента перед возвра- щением результатов. При значении FALSE поставщик вернет ошибку WSA_
LOJNCOMPLETE. Как только перекрытая операция завершится, поставщик должен присвоить значения параметрам WSPGetOverlappedResult:
Ш lpcbTransfer значение поля InternalHigh структуры WSAOVERLAPPED,
сообщающее о количестве переданных операцией отправки или получе- ния байтов;
lpdwFlags — значение поля Offset структуры WSAOVERLAPPED, сообщающее о флагах, являющихся результатом операций WSPRecv или WSPRecvFrom;

4 4 4 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Я ipErrno — значение поля OffsetHigh структуры WSAOVERLAPPED, сообща- ющее о результирующем коде ошибки.
Листинг 14-3 демонстрирует один из способов реализации WSPGetOver-
lappedResult.
Листинг 14-3. Подробности реализации функции WSPGetOverlappedResult
BOOL WSPAPI WSPGetOverlappedResult(
SOCKET s,
LPWSAOVERLAPPED lpOverlapped,
LPDWORD lpcbTransfer,
BOOL fWait,
LPDWORD lpdwFlags,
LPINT IpErrno)
{
DWORD Ret;
if (lpOverlapped->Internal != WSS_OPERATION_IN_PROGRESS)
{
•lpcbTransfer = lpOverlapped->InternalHigh;
•lpdwFlags = lpOverlapped->Offset;
•IpErrno = lpOverlapped->OffsetHigh;
return(lpOverlapped->OffsetHigh == 0 ? TRUE : FALSE);
}
else
{
if (fWait)
{
Ret = WaitForSingleObject(lpOverlapped->hEvent,
INFINITE);
if ((Ret == WAIT_0BJECT_0) &&
(lpOverlapped->Internal !=
WSS_OPERATION_IN_PROGRESS))
{
•lpcbTransfer = lpOverlapped->InternalHigh;
•lpdwFlags = lpOverlapped->Offset;
•IpErrno = lpOverlapped->OffsetHigh;
return(lpOverlapped->OffsetHigh == 0 ? TRUE :
FALSE);
}
else
•IpErrno = WSASYSCALLFAILURE;
>
else
•IpErrno = WSA_IO_INCOMPLETE;
}
return FALSE;

Г Л А В А 14 Интерфейс Winsock 2 SPI
445
Процедуры завершения. Завершая основанный на процедурах перекры- тый ввод-вывод, SPI-клиент вызывает одну из функций ввода-вывода со струк- турой WSAOVERIAPPED и указателем WSAOVERLAPPED COMPLETIONJROUTINE.
Ваш поставщик службы отвечает за управление запросом перекрытого вво- да-вывода. Когда запрос завершается, поставщик должен уведомить поток вызывающей программы о завершении перекрытого ввода-вывода, исполь- зуя Win32-MexaHH3M асинхронного вызова процедур (asynchronous procedure call, APC). APC требует, чтобы поток вызывающей программы был в «тревож- ном» состоянии ожидания (см. главу 8). Заканчивая обслуживание заверше- ния основанного на процедурах перекрытого запроса, поставщик должен уведомить SPI-клиента через перекрестный вызов функции WPUQueueApo.
int WPUQueueApc(
LPWSATHREADID lpThreadld,
LPWSAUSERAPC lpfnUserApc,
DWORD dwContext,
LPINT lpErrno
);
Параметр lpThreadld представляет структуру WSATHREADID SPI-клиента,
передаваемую исходным вызовом ввода-вывода и обеспечивающую заверше- ние процедуры. Параметр lpfnUserApc — указатель на промежуточную функ- цию WSAUSERAPC, которую поставщик должен предоставить для обратного вызова SPI-клиенту. Промежуточная функция вызывает WSAOVERLAPPED_
COMPLETION_ROUTINE SPI-клиента, предоставленную исходным перекры- тым вызовом. Прототип промежуточной функции WSAUSERAPC определен так-.
typedef void (CALLBACK FAR * LPWSAUSERAPC)(DWORD dwContext);
Заметьте, что это определение функции содержит только один параметр —
dwContext. Когда SPI-клиент вызывает эту функцию, dwContext содержит ин- формацию, первоначально переданную в параметре dwContext в WPUQueue-
Арс. По существу, dwContext позволяет передавать структуру данных, содер- жащую любые нужные элементы информации, при вызове WSAOVERLAP-
PED_COMPLETION_ROUTINE (см. главу 8):
void CALLBACK CompletionROUTINE(
IN DWORD dwError,
IN DWORD cbTransferred,
IN LPWSAOVERLAPPED lpOverlapped,
IN DWORD dwFlags
Ваш поставщик передает через параметр dwContext функции WPUQueue-
Арс следующую информацию:
Ж состояние перекрытой операции как код ошибки Winsock;
• число переданных через перекрытую операцию байт;
• структуру WSAOVERLAPPED вызывающей программы;
• флаги вызывающей программы, переданные вызову ввода-вывода.

4 4 6 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Теперь поставщик может успешно вызвать WSAOVERLAPPEDjCOMPLETI-
ON ROUTINE клиента SPI из промежуточной процедуры завершения.
Порты завершения. Модель ввода-вывода порта завершения Winsock 2
реализована в модуле Ws2_32.dll. Модель порта завершения основана на модели перекрытого ввода-вывода, организованной на процедурах. Поэто- му, чтобы ваш поставщик службы управлял моделью порта завершения, не требуется никаких дополнительных указаний.
Управление перекрытым вводом-выводом. Когда SPI-клиент вызы- вает ваш многоуровневый поставщик, использующий любую из описанных моделей перекрытого ввода-вывода, администратор перекрытия поставщи- ка в свою очередь вызывает нижестоящий поставщик, который использует метод перекрытого ввода-вывода, основанный на событиях или на порте завершения. Если ваш поставщик выполняется под управлением Windows NT
или Windows 2000, мы рекомендуем использовать порты завершения для перекрытого ввода-вывода на нижестоящем поставщике. В Windows 95 и Win- dows 98 следует использовать только основанный на событиях перекрытый ввод-вывод. Для работы с перекрытыми событиями вашему поставщику до- ступны следующие три вызова:
WSAEVENT WPUCreateEvent(LPINT lpErrno);
BOOL WPUResetEvent(WSAEVENT hEvent, LPINT lpErrno);
BOOL WPUCloseEvent(WSAEVENT hEvent, LPINT lpErrno);
Функция WPUCreateEvent создает и возвращает объект события, которое находится в режиме сброса вручную — точное подобие функции WSACreate-
Event (см. главу 8). Если WPUCreateEvent не сможет создать объект события,
возвращается NULL, а параметр lpErrno будет содержать определенный код ошибки Winsock. Функция WSPResetEvent, точно так же, как WSAResetEvent,
переводит объект события (параметр hEvent) из незанятого в занятое состо- яние. Функция WPUCloseEvent — аналог WSACloseEvent, она освобождает все рабочие ресурсы, связанные с описателем объекта события.
В примере LSP на прилагаемом компакт-диске основанный на событиях перекрытый ввод-вывод используется для управления всеми действиями пе- рекрытого ввода-вывода для SPI-клиента. Это позволяет примеру функцио- нировать под Windows 2000, Windows NT, Windows 98 и Windows 95- Важ- но, что в этом примере операции перекрытого ввода-вывода обслуживает только один поток, что ограничивает возможности поставщика: он может обслуживать одновременно не более чем WSA_MAXIMUM_WAIT_EVENTS (64)
объектов событий.
Чтобы преодолеть этот барьер, создайте несколько потоков обслужива- ния. А еще лучше (особенно если вы разрабатываете поставщик для Windows
NT и Windows 2000) — используйте вместо основанного на событиях пере- крытого ввода-вывода порты завершения.
Расширенные функции
Библиотека Winsock Mswsock.lib обеспечивает приложения расширенными функциями, увеличивающими возможности Winsock. В настоящее время поддерживаются следующие расширенные функции:

Г Л А В А 14 Интерфейс Winsock 2 SPI
447
AcceptEx WSAID_ACCEPTEX;
Ш GetAcceptExSockaddrs — WSAID _GETACCEPTEXSOCKADDRS\
TransmitFile WSAIDJTRANSMITFILE;
Ш WSARecvEx не связана с GUID.
Когда приложение, связанное с Mswsock.lib, использует AcceptEx, GetAc-
ceptExSockaddrs и TransmitFile, оно неявно вызывает функцию WSPIoctl вашего поставщика, используя SIOjGET_EXTENSION_FUNCTION_POINTER. Функция
WSPIoctl определена так:
int WSPIoctl(
SOCKET s,
DWORD dwIoControlCode,
LPVOID lpvInBuffer,
DWORD cblnBuffer,
LPVOID lpvOutBuffer,
DWORD cbOutBuffer,
LPDWORD lpcbBytesReturned,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadld,
LPINT lpErrno
Когда происходит вызов, параметру dwIoControlCode присваивается зна- чение SIO_GET_EXTENSIONJUNCTION_POINTER. Параметр lpvInBuffer содер- жит указатель на GUID, который определяет расширенную функцию для нужд Mswsock.lib (значения GUID, перечисленные в списке, определяют под- держиваемые в настоящее время расширенные функции). Если это значение
GUID соответствует любому из определенных значений, ваш поставщик дол- жен вернуть через lpvOutBuffer указатель на реализацию этой расширенной функции. Остальные параметры для управления расширенными функциями в SPI прямо не используются.
Мы отметили, что функция WSARecvEx не имеет GUID. Дело в том, что
WSARecvEx вызывает не WSPIoctl, а прямо WSARecv. В результате, поставщик не может напрямую контролировать расширенную функцию WSARecvEx.
Установка поставщиков транспортной службы
Установка поставщиков транспортной службы включает разработку просто- го приложения для вставки многоуровневого или базового поставщика в каталог поставщиков служб Winsock 2. Многоуровневый и базовый поставщи- ки транспорта устанавливаются по-разному. Программа установки просто настраивает поставщик в базе данных системной конфигурации Winsock 2,
которая является каталогом всех установленных поставщиков службы. База данных конфигурации дает Winsock 2 знать, что поставщик существует, и определяет тип его службы. Winsock 2 использует эту БД, чтобы определить,
какие поставщики службы следует загрузить при создании сокета приложе- нием Winsock. Ws2_32.dll ищет в базе данных первый поставщик, соответ-

4 4 8 ЧАСТЬ II Интерфейс прикладного программирования Winsock /
ствующий сокетным входным параметрам вызовов socket и WSASocket (таким как семейство адреса, тип сокета и протокол) Как только подходящая запись найдена, Ws2_32 dll загружает соответствующую DLL-библиотеку поставщи- ка службы, определенную в каталоге
По существу, для успешных установки и управления записью поставщика службы в БД поставщиков необходимы четыре функции конфигурации SPI
Каждая функция начинается с префикса WSC WSCEnumProtocols, WSCIns-
tallProvider, WSCWnteProviderOrder, WSCDelnstallProvider
Эти функции работают с базой данных, используя структуру WSAPROTO-
COLJNFOW (см главу 5) При установке поставщика транспортной службы нас прежде всего интересуют поля Providerld, dwCatalogEntryld и Protocol-
Cham этой структуры Поле Providerld — GUID, позволяющий уникально определять и устанавливать поставщик в любой системе Поле dwCatalog-
Entryld просто определяет каждую структуру WSAPROTOCOLJNFOW записи каталога в БД по уникальному числовому значению Поле ProtocolChain оп- ределяет, является ли структура WSAPROTOCOLJNFOW записью каталога для базового, многоуровневого или цепочки протоколов поставщика Поле Pro-
tocolChain — структура WSAPROTOCOLCHAIN
typedef struct {
int ChainLen,
DWORD ChainEntnes[MAX_PR0TOCOL_CHAIN],
} WSAPROTOCOLCHAIN, FAR * LPWSAPROTOCOLCHAIN,
Поле ChainLen определяет, представляет ли запись каталога многоуров- невый (ChamLen = 0), основной (ChainLen = 1) поставщик, или цепочку про- токолов поставщика (ChamLen > 1) Цепочка протоколов (protocol chain) —
специальная запись каталога, задающая, как поместить многоуровневый по- ставщик служб между Winsock и другими поставщиками службы (рис 14-2)
Многоуровневые и основные поставщики имеют только по одной записи ка- талога в БД Последнее поле — ChamEntnes, представляет собой массив иден- тификаторов каталога, используемых для описания порядка загрузки постав- щиков в цепочке протоколов Когда в ходе создания сокета Ws2_32 dll ищет в каталоге соответствующий поставщик, она просматривает только записи цепочек протоколов и базовых поставщиков Записи многоуровневых постав- щиков (ChamLen - 0) игнорируются — они существуют только для сопоставле- ния многоуровневого поставщика цепочке протоколов в записях этих цепочек.
Установка базового поставщика
Чтобы установить основной поставщик, создайте структуру WSAPROTOCOL_
INFOW записи каталога, которая представляет этот поставщик Заполните поля в этой структуре информацией об атрибутах протокола Не забудьте присвоить значение 1 полю ChainLen структуры ProtocolChain Когда структу- ра определена, поместите ее в каталог, используя функцию WSClnstallProvider
int WSCInstallProvider(
const LPGUID lpProviderld,
const LPWSTR lpszProviderDllPath,

Г Л А В А 14 Интерфейс Winsock 2 SPI 449
const LPWSAPROTOCOL_INFOW lpProtocolInfoList,
DWORD dwNumberOfEntries,
LPINT lpErrno
).
Параметр ipProviderld — GUID, позволяющий идентифицировать постав- щик для каталога Winsock Параметр ipszProviderDllPath — строка, содержа- щая загрузочный путь к DLL поставщика Строка может включать перемен- ные окружения, такие как %SystemRoot% Параметр lpProtocolInfoList пред- ставляет массив структур данных WSAPROTOCOLJNFOW для помещения в ка- талог Устанавливая основного поставщика, вы можете просто назначить структуре WSAPROTOCOLJNFOWпервый элемент массива Параметр dwNum-
berOfEntnes содержит количество записей в массиве lpProtocolInfoList, пара- метр IpErmo — код ошибки, если выполнение этой функции не удалось Тогда возвращается SOCKETERROR
Установка многоуровневого поставщика
Чтобы установить многоуровневый поставщик службы, создайте две струк- туры WSAPROTOCOLJNFOW записей в каталоге Одна будет представлять ваш многоуровневый поставщик (например, длина цепочки протоколов, равная
0), а другая — цепочку протоколов (например, длина цепочки протоколов,
большая 1), соединяющую многоуровневый поставщик с основным Эти две структуры следует инициировать со свойствами структуры WSAPROTOCOL_
INFOW записи каталога существующего поставщика службы Запись вы мо- жете найти с помощью функции WSCEnumProtocols
int WSCEnumProtocols(
LPINT lpiProtocols,
LPWSAPROTOCOL.INFOW lpProtocolBuffer,
LPDWORD lpdwBufferLength,
LPINT lpErrno
r(>v
),
Параметр lpiProtocols — необязательный массив значений Если lpiPro-
tocols содержит NULL, возвращается информация по всем доступным прото- колам, если нет — только по протоколам, перечисленным в массиве Пара- метр lpProtocolBuffer — предоставленный приложением буфер, заполненный структурами WSAPROTOCOLJNFOW из каталога Winsock 2 Входящий пара- метр lpdwBufferLength содержит количество байт в переданном функцией
WSCEnumProtocols буфере lpProtocolBuffer При выходе в этот параметр зано- сится минимальный размер буфера, который можно передать WSCEnum-
Protocols для определения всей запрошенной информации Параметр lpErrno
содержит информацию об ошибке, если выполнение функции неудачно и возвращено SOCKET_ERROR Располагая записью в каталоге для поставщика,
который вы собираетесь поместить сверху, скопируйте свойства структуры
WSAPROTOCOLJNFOW поставщика, во вновь созданные структуры
После инициализации поместите в каталог запись многоуровневого по- ставщика, используя функцию WSCInstallProvider Затем найдите идентифи-

450 ЧАСТЬ II Интерфейс прикладного программирования Winsock катор каталога, который присваивается этой структуре после установки, пе- ребрав записи в каталоге с помощью WSCEnumProtocols. Эта запись может затем использоваться при помещении в каталог записи цепочки протоколов,
связывающей ваш многоуровневый поставщик с другим поставщиком. Затем для установки включенного в цепочку поставщика вызывается функция WSC-
InstallProvider. Этот процесс иллюстрирует следующий псевдокод:
WSAPROTOCOL_INFOW LayeredProtocolInfoBuff,
ProtocolChainProtoInfo,
BaseProtocolInfoBuff;
// Получение BaseProtooolInfoBuff с помощью WSCEnumProtocolsO
memcpy (&LayeredProtocolInfoBuff , &BaseProtocolInfoBuff,
sizeof(WSAPROTOCOL_INFO));
LayeredProtocolInfoBuff.dwProviderFlags = PFL_HIDDEN;
LayeredProtocolInfoBuff.Providerld = LayeredProviderGuid;
// Эта запись будет заполнена системой
LayeredProtocolInfoBuff.dwCatalogEntryld = 0;
LayeredProtocolInfoBuff.ProtocolChain.ChainLen =
LAYERED_PROTOCOL;
WSCInstallProvider(&LayeredProviderGuid, L"lsp.dll",
JLayeredProtocolInfoBuff, 1, & install error);
tot
// Определение идентификатора каталога многоуровневого поставщика с ' '
// использованием функции WSCEnumProtocolsO <\
for ( 1 = 0 ; 1 < TotalProtocols; 1++)
if (memcmp (&ProtocolInfo[l].Providerld, &ProviderGuid, r-
sizeof (GUID))==O) . ^
{
LayeredCatalogld = Protocollnfo[i].dwCatalogEntryld;
break;
Memcpy(&protocolChainProtoInfo, &BaseProtocolInfoBuff,
sizeof(WSAPR0T0C0L_INF0));
ProtocolChainProtoInfo.ProtocolChain.ChainLen = 2;
ProtocolChainProtoInfo.ProtocolChain.ChainEntries[0] =
LayeredProvideProtocolInfo.dwCatalogEntryld;
ProtocolChalnProtoInfo.ProtocolChain.ChainEntries[1] =
BaseProtooolInfoBuff.dwCatalogEntryld;
WSCInstallProvider(
iChainedProviderGuid,
L"lsp.dll", // lpszProviderDHPath
&ProtooolChainProtoInfo, // lpProtocolInfoList

Г Л А В А 14 Интерфейс Winsock 2 SPI
451
1,
&install error
// dwNumberOfEntries
// lpErrno
Обратите внимание на флаг PFLHIDDENв структуре WSAPROTOCOLJNFOW.
Благодаря ему функция WSAEnumProtocols (см. главу 5) не включает каталог для многоуровневого поставщика в возвращаемый ею буфер.
Другой важный флаг, которым должна управлять программа установ- ки — XP1JFS_ HANDLES. Любой He-IFS-поставщик службы, использующий
WPUCreateSocketHandle для создания своих описателей сокета, не должен за- давать флаг XP1_IFS_HANDLES в структуре WSAPROTOCOLJNFOW. Для прило- жений Winsock отсутствие флага ХР1JFSJiANDLES — указание избегать ис- пользования функций ReadFile и WriteFile из-за потенциального снижения производительности.
Упорядочение поставщиков
Теперь вы должны решить, как Winsock 2 будет искать поставщиков служб в БД.
Большинство приложений Winsock определяют нужный протокол с помощью параметров вызова функций socket и WSASocket. Например, если приложение создает сокет, используя семейство адресов AFJNET и тип SOCK_STREAM, Win- sock 2 ищет заданный по умолчанию протокол TCP/IP, запись включенного в цепочку или базового поставщика в БД. Когда вы устанавливаете постав- щик службы, используя WSCInstallProvider, запись в каталоге автоматически становится последней в БД. Чтобы сделать поставщик службы стандартным поставщиком TCP/IP, упорядочите записи о поставщиках в базе данных и поместите запись цепочки протоколов перед другими поставщиками TCP/
IP. Для этого вызовите функцию WSCWriteProviderOrder.
int WSCWriteProviderOrder(
LPDWORD lpwdCatalogEntryld,
DWORD dwNumberOfEntries
Параметр lpwdCatalogEntryld принимает массив идентификаторов ката- лога, определяющих порядок его сортировки. Вы можете задать идентифи- каторы каталога, вызвав WSCEnumProtocols, как описано ранее. Параметр
dwNumberOfEntries — счетчик записей каталога в массиве. Эта функция воз- вращает ERROR_SUCCESS (0), если выполнена успешно, и код ошибки Win- sock — если нет.
Функция WSCWriteProviderOrder не входит в библиотеку Ws2_32.dll. Что- бы использовать ее, приложение должно быть скомпоновано с библиотекой
Sporder.lib. Модуль Sporder.dll не является частью ОС Windows, ищите DLL
поддержки в библиотеке Microsoft Developer Network (MSDN). Если вы ис- пользуете этот модуль, распространяйте его вместе с приложением.
Библиотека MSDN также предлагает удобную программную утилиту Spor- der.exe, которая позволяет просматривать и переупорядочивать записи ка- талога в БД Winsock 2 (рис. 14-3)-

452
ЧАСТЬ II Интерфейс прикладного программирования Wmsock
Senflee ptovtdws | tame Resatutron j
Layered TCP/IP ovei |MSAFD Тсрир [TCP/IP])
MSAFD Tcpp [TCP/IP]
MSAFD Tcpip [UDP/IP]
MSAFD Tcpip [RAW/IP]
RSVP UDP Service Provider
RSVP TCP Service Provider
MSAFD NetBIOS [\Device\NetBT TcpipJ28O1SCC0 DOAA11D28A53 8EE7542C0OOE}
MSAFD NetBIOS [\DevKe\NetBTlTcpipJ28D19Ci:0DOAA 11D28A53 8EE7542C000E}
MSAFD NetBIOS [SDeviceSNetBT_Tcp«>J28O13CC2-D(»A 11D2-8A53«E7542COOOE)
MSAFD NetBIOS [\DevrceSNetBT_TcppJ28O19CC2D0AA11D28AS3 8EE7542C000E)
MSAFD NetBIOS [\Device\NetBT_TcppJ49213832 DOAB 11D2 I3A53-00105424E14A)]
MSAFO NetBIOS |\Devce\NetBT TcpipJ49213832 DOAB 11D2 8А53-О01ОЕА24Е14АЦ
MSAFD NetBIOS [\Device\NefflTlTcr4> {49213831 DOAB 11D2 8A53OO1O5A24E14A(]
MSAFO NetBIOS |\Device\NetBT_Tcpip

<49213831 DOAB 11D2 8A53 00105A24E14A!]
MSAFD NetBIOS (\DevrceWetBT TcpipI(49213830DQAB 11D2 8A53 0010SA24E14A)]
MSAFD NetBIOS [\Devce\NelBTTcpipJ49213830 DQAB 11D2-8A53 00105A24E14AI]
Рис. 14-3. Конфигурация Winsock 2 после установки многоуровневого
поставщика на компьютер с Windows 2000
Удаление поставщика службы
Удалить поставщик службы из каталога Wmsock 2 не сложно Вызовите фун- кцию WSCDeinstallProvider
int WSCDeinstallProvider(
LPGUID lpProviderld,
LPINT lpErrno
Параметр lpProviderld представляет GUID удаляемого поставщика служ- бы Параметр lpErrno получает код ошибки Wmsock, если функция возвра- щает SOCKET_ERROR
При удалении поставщика службы вы должны учитывать один важный момент Всегда есть возможность, что многоуровневый поставщик службы включил идентификатор каталога вашего поставщика службы в свою цепоч- ку протокола Если это так, вы должны удалить идентификатор каталога из любых цепочек протоколов, которые ссылаются на ваш поставщик
Проблемы при установке многоуровневых поставщиков
Многоуровневые поставщики службы имеют огромный потенциал
Однако нынешняя спецификация Winsock 2 не отвечает на важный вопрос как многоуровневый поставщик службы может узнать, в каком месте вставить себя в цепочку протоколов, если находит другой мно- гоуровневый поставщик
Например, если вы хотите установить поставщик шифрования дан- ных в системе, которая уже содержит поставщик фильтрации URL, оче-

Г Л А В А 14 Интерфейс Winsock 2 SPI
453
видно, что первый должен быть вставлен ниже второго в сущест вую- щей цепочке протоколов Но проблема в том, что программа установ- ки поставщика не может выяснить, какую службу обеспечивает сущест- вующий поставщик, и поэтому не знает, куда вставить новый Это не приносит больших осложнений для управляемой сетевой среды, в ко- торой администраторы решают, какие поставщики и в каком порядке устанавливать Но широкому распространению многоуровневых по- ставщиков службы препятствует то, что безопасной может быть толь- ко установка многоуровневого поставщика службы непосредственно поверх основного и задание новой цепочки поставщиком протокола по умолчанию Такой подход гарантирует службу нового поставщика,
но удаляет существующий многоуровневый поставщик как заданную по умолчанию цепочку поставщиков
Есть еще одна проблема, которую не разрешает спецификация Win- sock 2 Она связана с первой, хотя и не столь серьезна Как существую- щие многоуровневые поставщики могут защитить себя от изменений в цепочке или узнать, что такие изменения происходят' Если цепочка протоколов многоуровневого поставщика не должна меняться, его разработчик может жестко запрограммировать порядок цепочки в рамках функции WSPStartup и определить поставщик как базовый, ука- зав значение 1 для параметра ProtocolChmn ChamLen структуры WSAP-
ROTOCOLJNFOWдля LSP
Поставщики службы пространства имен
В главе 10 мы рассмотрели, как приложение регистрирует и разрешает служ- бы в пространстве имен, что особенно важно для служб, которые можно динамически создавать в сети К сожалению, полезность существующих дос- тупных пространств имен ограничена Спецификация Winsock 2, однако,
предлагает вам метод создания собственных пространств имени, где вы смо- жете регистрировать и разрешать имена любым удобным для себя способом
Это можно сделать с помощью DLL, реализующей девять функций про- странства имен Все функции начинаются с префикса NSP и соответствуют функциям RNR (см главу 10) Например, эквивалент функции пространства имени WSASetSeruice — функция NSPSetService После создания DLL она уст а- навливается в системный каталог с GUID, идентифицирующим простран- ство имен Затем приложения могут регистрировать и запрашивать службы в вашем пространстве имен
Сначала мы расскажем, как установить поставщик пространства имен, а затем опишем функции, которые он должен реализовать Наконец, рассмот- рим типовой поставщик пространства имен, а также типовое приложение,
регистрирующее и разрешающее службы
Установка поставщика пространства имен
Поставщик пространства имен — это просто DLL, осуществляющая функции поставщика пространства имен Чтобы приложения могли использовать

4 5 4 ЧАСТЬ II Интерфейс прикладного программирования Winsock пространство имен, уведомите о нем систему с помощью функции WSCIns-
tallNameSpace. Установленный поставщик вы можете отключить или удалить из системного каталога с помощью функций WSAEnableNSProvider и WSA-
UnlnstallNameSpace.
Функция WSCInstallNameSpace j
Эта функция используется для установки поставщика в системный катался]
Она определена так: >
t
int WSCInstallNameSpace (
LPWSTR lpszldentifier,
LPWSTR lpszPathName,
DWORD dwNameSpace,
DWORD dwVersion,
LPGUID lpProviderld
);
Сразу бросается в глаза, что все строковые параметры представлены ши- рокими символами. На самом деле, с использованием строк широких сим- волов реализованы все поставщики пространства имен.
Параметр lpszldentifier — имя поставщика пространства имен, возвращае- мое функцией WSAEnumNameSpaceProviders (см. главу 10). Параметр lpsz-
PathName — расположение DLL Строка может включать системные перемен- ные, такие как %SystemRoot%. Параметр dwNameSpace — числовой иденти- фикатор пространства имен. Например, в заголовочном файле Nspapi.h оп- ределены константы других известных пространств имен, типа NS_SAP, для
IPX SAP. Параметр dwVersion задает номер версии пространства имен. Нако- нец, параметр lpProviderld — GUID, идентифицирующий поставщик про- странства имен.
В случае удачного выполнения WSCInstallNameSpace возвращает 0, иначе —
SOCKET_ERROR. Чаще всего это WSAEINVAL (пространство имен с данным
GUID уже существует) или WSAEACCESS (процесс вызова не имеет достаточ- ных полномочий). Задавать пространство имен могут только члены группы
Administrators.
Функция WSCEnableNSProvider
Эта функция используется для изменения состояния (включения или выклю- чения) поставщика пространства имен:
int WSCEnableNSProvider (
LPGUID lpProviderld,
BOOL fEnable
);
Параметр lpProviderld — идентификатор GUID пространства имен, кото- рое вы хотите изменить. Параметр fEnable содержит булево значение, ука- зывающее, следует ли включить или выключить поставщик. Выключенный поставщик не может обрабатывать запросы или регистрацию.

Г Л А В А 14 Интерфейс Winsock 2 SPI 455
При удачном выполнении WSCEnableNSProvider возвращает 0, иначе —
SOCKET_ERROR. Если GUID поставщика не действителен, возвращается ошиб- ка WSAEINVAL.
Функция WSCUnlnstallNameSpace
г
Эта функция удаляет поставщик пространства имен из каталога:
int WSCUnlnstallNameSpace ( LPGUID lpProviderld );
Параметр lpProviderld — GUID удаляемого пространства имен. Если GUID
не действителен, выполнение возвращается ошибка WSAEINVAL.
Реализация пространства имен
Пространство имен должно реализовать все девять функций, соответству- ющих функциям RNR. Кроме того, следует разработать метод для постоян- ного сохранения данных, то есть поддерживать данные вне экземпляра
DLL Каждый загружающий DLL процесс получает свой собственный сег- мент данных — это означает, что сохраненные в рамках DLL данные не могут быть разделены между экземплярами (на самом деле, совместное использование информации загрузившими DLL приложениями возможно,
но не одобряется).
Дополнительную информацию по библиотекам DLL можно найти в кни- ге «Windows для профессионалов» Джеффри Рихтера (М.: «Русская Редакция».
2001). Как упоминалось в главе 10, существуют три типа пространства имен:
динамическое, постоянное и статическое. Очевидно, реализовать статичес- кое пространство не стоит, так как оно не допускает программную регист- рацию служб. Далее мы рассмотрим некоторые аспекты поддержки данных,
которые пространство имен должно сохранить.
Очень важно использовать строки широких символов во всех функциях поставщика пространства имен: не только строковых параметров функций,
но также и строк в RNR-структурах, типа WSAQUERYSET и WSASERVICECLASS-
INFO. Вы можете спросить, возможно ли это — ведь когда приложение ре- гистрирует или разрешает имя, оно вправе использовать и обычную версию
(ASCII), и версию с широкими символами (UNICODE) функций и RNR-струк- тур. На самом деле, работает любая версия, так как все запросы ASCII прохо- дят промежуточный уровень, преобразующий все строки в строки широких символов.
Это верно и для вызова функции, и для ее возвращения. То есть если вы- зывающему приложению возвращается WSAQUERYSET (с помощью функции
WSALookupServiceNexf), то любые данные, возвращенные поставщиком про- странства имен, изначально находятся в виде UNICODE и преобразуются в
ASCII перед возвратом из функции. Так что если ваши приложения исполь- зуют RNR-функции, вызов версий с широкими символами будет быстрее, так как не требует никаких преобразований.
Из девяти функций, которые должен реализовать поставщик простран- ства имен, только семь соответствуют RNR-функциям Winsock 2 (табл. 14-2).

456 ЧАСТЬ II Интерфейс прикладного программирования Winsock
Табл. 14-2. Соответствие функций регистрации и разрешения имен
в Winsock 2 функциям поставщика пространства имен
функция Winsock Эквивалентная функция поставщика пространства имен
WSAInstallServiceClass NSPInstallServtceClass
WSARemoveServiceClass NSPRemoveServiceClass
WSAGetServiceClassInfo NSPGetServiceClassInfo
! T
*"
WSASetService NSPSetService }$1
WSALookupServiceBegm NSPLookupServiceBegm
WSALookupServiceNext NSPLookupServiceNext
э г
WSALookupServiceEnd NSPLookupServiceEnd
Оставшиеся две функции применяются для инициализации и очистки. Когда пространство имен указано в системе, приложения могут использовать его,
определяя соответствующий GUID или идентификатор пространства имен.
Тогда приложение вызывает стандартную RNR-функцию Winsock 2, как описа- но в главе 10. При этом вызывается эквивалентная функция поставщика про- странства имен Например, при вызове из приложения функции WSAInstall-
ServiceClass, которая ссылается на GUID пользовательского пространства имен,
вызывается функция NSPInstallServiceClass для этого поставщика
Далее мы рассмотрим все функции пространства имен.
Функция NSPStartup
NSPStartup вызывается каждый раз, когда загружается DLL поставщика про- странства имен. Ваша реализация пространства имен должна включать эту функцию, экспортируемую из DLL. При этом можно выделить для каждой
DLL все структуры данных, которые требуются для работы поставщика. Про- тотип NSPStartup выглядит так:
int NSPStartup (
LPGUID lpProviderld,
LPNSP_ROUTINE lpnspRoutines
);
Первый параметр — lpProviderld, является GUID для этого поставщика пространства имен. Параметр lpnspRoutines — структура NSP_ROUTINE, кото- рую должна заполнить ваша реализация этой функции. Эта структура обес- печивает указатели функции для восьми других функций пространства имен поставщика. Объект NSP_ROUTINE определен так:
typedef struct _NSP_ROUTINE
{
DWORD cbSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
LPNSPCLEANUP NSPCleanup;
LPNSPLOOKUPSERVICEBEGIN NSPLookupServiceBegin;
LPNSPLOOKUPSERVICENEXT NSPLookupServiceNext;
LPNSPLOOKUPSERVICEEND NSPLookupServiceEnd;

Г Л А В А 14 Интерфейс Winsock 2 SPI
457
LPNSPSETSERVICE NSPSetService;
LPNSPINSTALLSERVICECLASS NSPInstallServiceClass;
LPNSPREMOVESERVICECLASS NSPRemoveServiceClass;
LPNSPGETSERVICECLASSINFO NSPGetServiceClassInfo;
} NSP_ROUTINE, FAR * LPNSP_ROUTINE;
Первое поле структуры — cbSize, указывает размер структуры NSP_ROU-
TINE Следующие два поля — dwMajorVersion и dwMinorVersion, включены для управления версиями поставщика. Управление версиями произвольно и не служит никакой другой цели Поставщик присваивает остальным записям соответствующие указатели на функции Например, свой адрес функции
NSPSetService (независимо от того, что скрывается под этим именем) — NSP-
SetService. Имена функций поставщика могут быть произвольными, но их параметры и возвращаемые типы должны соответствовать определению поставщика
Единственное действие, требуемое NSPStartup^ — заполнить структуру
NSP_ROUTINE. Когда поставщик успешно завершает эту и любые другие про- цедуры инициализации, он возвращает NO_ERROR Если произошла ошиб- ка, NSPStartup возвращает SOCKETJERROR и выясняет код ошибки Winsock
Например, если ошибка связана с распределением памяти, поставщик вызы- вает WSASetLastError с параметром WSA_NOT_ENOUGH_MEMORYu затем воз- вращает SOCKET_ERROR
Сейчас, наверное, самое время обсудить обработку ошибок в DLL постав- щика. Все функции, которые вы должны реализовать для поставщика, возвра- щают NOERROR в случае успеха и SOCKETERROR — при неудаче. Если вы видите, что вызов возвращает ошибку, назначьте соответствующий код ошиб- ки Winsock Иначе любое приложение, пытающееся регистрировать или вызывать службы, используя ваш поставщик пространства имен, сообщит о неудаче функции RNR, но WSAGetLastError вернет 0 Это вызовет проблемы у приложений, которые пытаются корректно обрабатывать ошибки, 0 — бе- зусловно не то значение, которое ожидается при ошибке
Функция NSPCIeanup
Эта процедура вызывается при выгрузке DLL поставщика. С ее помощью вы можете освобождать любую память, выделенную процедурой NSPStartup
Функция NSPCIeanup определена так-
int NSPCIeanup ( LPGUID lpProviderld );
Единственный параметр — GUID вашего поставщика пространства имен
От вас требуется лишь очистка всей динамически выделенной памяти
Функция NSPInstallServiceClass
Функция NSPInstallServiceClass соответствует WSAInstallServiceClass и отвеча- ет за регистрацию класса служб int NSPInstallServiceClass (
LPGUID lpProviderld,

458 ЧАСТЬ II Интерфейс прикладного программирования Winsock
LPWSASERVICECLASSINFOW lpServiceClassInfo
);
Первый параметр — GUID поставщика. Параметр lpServiceClassInfo — ре- гистрируемая структура WSASERVICECLASSINFOW. Ваш поставщик должен поддерживать список классов служб и гарантировать, что не существует класса служб, использующего тот же самый GUID в структуре WSASERVICEC-
LASSINFOW. Если же этот GUID уже используется, поставщик должен вернуть ошибку WSAEALREADY. Иначе ему придется поддерживать этот класс служб так, чтобы другие операции RNR могли ссылаться на него.
Большинство оставшихся функций поставщика пространства имен ссы- лаются на определенный класс служб.
Функция NSPRemoveServiceClass
Эта функция дополняет NSPInstallServiceClass и соответствует WSARemove-
ServiceClass. Она удаляет указанный класс служб:
mt NSPRemoveServiceClass (
LPGUID lpProviderld,
LPGUID lpServiceClassId
);
Как и в предыдущей функции, первый параметр — GUID поставщика. Вто- рой — lpServiceClassId, GUID удаляемого класса служб. Поставщик должен удалить данный класс служб из своей памяти. Если класс служб, указанный параметром lpServiceClassId, не найден, поставщик сгенерирует ошибку WSA-
TYPE_NOT_FOUND.
Функция NSPGetServiceClasslnfo
Функция NSPGetServiceClasslnfo соответствует WSAGetServiceClassInfo. Она из- влекает связанную с GUID структуру WSANAMESPACEJNFOW.
int NSPGetServiceClasslnfo (
LPGUID lpProviderld,
LPOWORD lpdwBufSize,
LPWSASERVICECLASSINFOW lpServiceClassInfo
);
Опять первый параметр — GUID поставщика. Параметр lpdwBufSize указы- вает количество байт, содержащихся в третьем параметре — lpServiceClassInfo.
Третий входной параметр — структура WSASERVICECLASSINFOW, содержит ус- ловия поиска, определяющие класс служб для возврата. В этой структуре мо- гут находиться имя или GUID класса служб для возврата. Если поставщик об- наруживает соответствие условиям, он должен вернуть структуру WSASER-
VICECLASSINFOW в параметре lpServiceClassInfo и обновить параметр lpdw-
BufSize, указав количество возвращаемых байт.
Если не найдены искомые классы служб, будет выдана ошибка WSATYPE_
NOT_FOUND. Если же соответствующий класс служб найден, но предоставлен- ный буфер слишком мал, поставщик должен обновить параметр lpdwBufSize,
указав правильное количество требуемых байт, и выдать ошибку WSAEFAULT.

1   ...   33   34   35   36   37   38   39   40   ...   50


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