Главная страница
Навигация по странице:

  • SOCKET WPUCreateSocketHandle( DWORD dwCatalogEntryld, ^ DWORD dwContext, LPINT lpErrno *

  • Поддержка модели ввода-вывода Winsock

  • Блокирующий и неблокирующий ввод-вывод

  • Листинг 14-2. Подробности реализации WSPSelect int WSPAPI WSPSelect( int nfds

  • привязок сокетов для вышестоящего и нижестоящего

  • ГЛАВА 14 Интерфейс Winsock 2 SPI 439

  • Модель WSAAsyncSelect

  • BOOL WPUPostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM IParam );

  • BOOL WPUSetEvent ( WSAEVENT hEvent, LPINT lpErrno

  • WSAEVENT WPUCompleteOverlappedRequest( SOCKET s

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


    Скачать 2.88 Mb.
    НазваниеЭ. Джонс, Д. Оланд
    АнкорПрограммирование в сетях Windows.pdf
    Дата12.10.2017
    Размер2.88 Mb.
    Формат файлаpdf
    Имя файлаПрограммирование в сетях Windows.pdf
    ТипКнига
    #9346
    страница36 из 50
    1   ...   32   33   34   35   36   37   38   39   ...   50
    ГЛАВА 14 Интерфейс Winsock 2 SPI
    435
    He-IFS-поставщик
    Если вы разрабатываете многоуровневый поставщик и намерены контроли- ровать каждую проходящую через сокет операцию чтения и записи, вам луч- ше выбрать He-IFS-поставщик. He-IFS-поставщики создают описатели соке- та, используя обратный вызов WPUCreateSocketHandle. Описатели сокета, со- зданные WPUCreateSocketHandle, подобны описателям IFS-поставщика, так как позволяют приложениям Winsock использовать функции ReadFile и WriteFile
    на сокете. Однако с этими двумя функциями связана существенная пробле- ма ввода-вывода, так как архитектура Winsock 2 должна выполнять переад- ресацию ввода-вывода функциям WSPRecv и WSPSend поставщика службы.
    WPUCreateSocketHandle определена таю
    SOCKET WPUCreateSocketHandle(
    DWORD dwCatalogEntryld, ^
    DWORD dwContext,
    LPINT lpErrno *
    Поле dwCatalogEntryld определяет идентификатор каталога вашего по- ставщика службы. Параметр dwContext позволяет сопоставлять данные по- ставщика с описателем сокета. Заметьте: dwContext дает большую свободу в отношении информации, которую вы можете связать с описателем сокета.
    В примере LSP на прилагаемом компакт-диске это поле используется для сохранения количества переданных и полученных данных. Winsock обеспе- чивает обратный вызов функции WPUQuerySocketHandleContext, которая может применяться для извлечения связанных с сокетом данных поставщи- ка, сохраненных в поле dwContext. Этот обратный вызов определен так:
    :
    int WPUQuerySocketHandleContext(
    f
    SOCKET s, ' ,]
    LPDWORD lpContext, ф j
    LPINT lpErrno
    Параметр 5 — описатель сокета, переданный SPI-клиентом (первоначаль- но созданный WPUCreateSocketHandle), для которого вы хотите восстановить сокетные данные поставщика. Параметр lpContext получает данные постав- щика, первоначально переданные WPUCreateSocketHandle. Параметр lpErrno
    в обеих функциях получает определенный код ошибки Winsock, если выпол- нение этих функций не удается. Если выполняется WPUCreateSocketHandle,
    возвращается значение INVALID_SOCKET, если WPUQuerySocketHandleContext
    значение SOCKETERROR.
    Поддержка модели ввода-вывода Winsock
    Winsock предлагает несколько моделей, которые могут использоваться для управления вводом-выводом через сокет. С точки зрения поставщика служб,
    каждая модель требует обратного вызова какой-либо SPI-функции из биб- лиотеки Ws2_32.dll, доступной из уже упомянутого параметра UpcallTable в

    436 ЧАСТЬ II Интерфейс прикладного программирования Winsock
    WSPStartup. Если вы разрабатываете простой многоуровневый IFS-постав- щик, принципы, описанные в следующих разделах, не применяются — вы просто полагаетесь на нижестоящий поставщик, чтобы управлять всем вво- дом-выводом для каждой модели. Рассматриваемые принципы относятся к любому другому типу поставщика. Мы сосредоточимся на аспектах разра- ботки многоуровневого He-IFS-поставщика службы.
    Блокирующий и неблокирующий ввод-вывод
    Блокирующий ввод-вывод — самая простая форма ввода-вывода в Winsock 2.
    Любая операция ввода-вывода с блокируемым сокетом не будет возвраще- на, пока не завершится. Поэтому любой поток может выполнять одновре- менно только одну операцию ввода-вывода. Например, когда SPI-клиент вы- зывает функцию WSPRecv в блокируемом режиме, ваш поставщик должен только передать запрос прямо вызову WSPRecv следующего поставщика.
    Функция WSPRecv вашего поставщика вернется, только когда вызов WSPRecv
    завершит следующий поставщик.
    Реализация блокируемого ввода-вывода не сложна, но все же обратите вни- мание на обратную совместимость с блокирующими вызовами Winsock 1.1.
    Вызовы WSASetBlockingCall и WSACancelBlocking API удалены из спецификации
    Winsock 2. Впрочем, WSPCancelBlockingHook все-таки может быть вызвана биб- лиотекой Ws2_32.dll, если приложение Winsock 1.1 вызывает функции WSASet-
    BlockingHook и WSACancelBlockingCall. В многоуровневом поставщике службы вы можете просто передать вызов WSPCancelBlockingHook запросу основно- го поставщика. Если вы реализуете основной поставщик, и происходит зап- рос блокирования, следует предусмотреть механизм периодического вызо- ва функции WPUQueryBlockingCallback:
    int WPUQueryBlockingCallback( '
    DWORD dwCatalogEntryld,
    LPBLOCKINGCALLBACK FAR .lplpfnCallback,
    LPDWORD lpdwContext,
    LPIKT lpErrno
    );
    Параметр dwCatalogEntryld получает идентификатор записи каталога ваше- го поставщика, как описано в функции WSPStartup. Параметр iplpfnCallback
    указатель на функцию обработчика блокирующих прерываний приложения,
    которую вы должны периодически вызывать для предотвращения реально- го блокирования блокирующих вызовов приложения. Эта функция обратно- го вызова имеет следующую форму:
    typedef BOOL (CALLBACK FAR • LPBLOCKINGCALLBACK)(
    DWORD dwContext j
    );
    Когда ваш поставщик периодически вызывает IplpfnCallback, значение
    lpdwContext передается в параметр функции обратного вызова dwContext.
    Заключительный параметр WPUQueryBlockingCallback lpErrno, возвраща- ет код ошибки Winsock, если функции возвращают значение SOCKETJERROR.

    ГЛАВА 14 Интерфейс Winsock 2 SPI 437
    Модель select
    Модель ввода-вывода select требует, чтобы поставщик управлял структурами
    fdset для параметров readfds, writefds и exceptfds функции WSPSelect:
    int WSPSelect( •
    mt nfds,
    fd_set FAR *readfds,
    v
    )
    fd_set FAR •writefds, . 503 '• 0- 08
    fd_set FAR «exceptfds, • -- u
    const struct timeval FAR «timeout,
    ; t m r
    -'- и
    LPINT lpErrno - n
    -
    ). Oei :nk
    По сути, тип данных fd_set представляет собой совокупность сокетов.
    Когда клиент вызывает ваш поставщик, используя WSPSelect, он передает опи- сатели сокета одному или нескольким этим наборам. Поставщик отвечает за определение сетевой активности на каждом из перечисленных сокетов.
    Для многоуровневых He-IFS-поставщиков это требует создания трех по- лей данных fdset и привязки описателей сокета клиента SPI к описателям сокета нижестоящего поставщика в каждом наборе. Как только все наборы определены, поставщик вызывает функцию WSPSelect нижестоящего постав- щика. Когда нижестоящий поставщик завершает работу, ваш поставщик дол- жен определить, на каких сокетах есть события, ожидающие очереди в каж- дом из полей fdset. Существует полезный обратный вызов WPUFDIsSet, ко- торый определяет установленные сокеты нижестоящего поставщика. Этот обратный вызов подобен макрокоманде FDJSSET (см. главу 8):
    int WPUFDIsSet(
    SOCKET s,
    ll 1 0
    J
    FD_SET FAR «set . '
    );
    Параметр s — сокет, который вы ищете в наборе. Параметр set — факти- ческий набор описателей сокета. Проверив содержимое каждого набора,
    переданное нижестоящему поставщику, ваш поставщик должен сохранить соответствие сокета верхнего поставщика нижестоящему. Когда нижестоя- щий поставщик завершает вызов WSPSelect, легко определить, какие сокеты ожидают ввода-вывода для вышестоящего поставщика.
    Определив сокеты нижестоящего поставщика, у которых в очереди есть сетевые события, обновите исходные наборы fdjset, переданные исходным
    SPI-клиентом. Файл Ws2spi.h описывает три макрокоманды — FD_CLR, FD_SET
    и FD_ZERO, которые могут использоваться для управления исходными набо- рами (см. главу 8). Листинг 14-2 демонстрирует одну из возможных реали- заций WSPSelect.
    Листинг 14-2. Подробности реализации WSPSelect
    int WSPAPI WSPSelect(
    int nfds,
    см. след. стр.

    4 3 8 ЧАСТЬ II Интерфейс прикладного программирования Winsock
    Листинг 14-2. {продолжение)
    fd_set FAR • readfds,
    fd_set FAR * writefds,
    fd_set FAR * exceptfds,
    const struct timeval FAR * timeout,
    LPINT lpErrno)
    {
    SOCK_INFO *SocketContext;
    u_int I;
    u_int count;
    int Ret;
    int HandleCount;
    // Построение таблицы привязок сокетов для вышестоящего и нижестоящего
    поставщиков struct
    {
    SOCKET ClientSocket;
    SOCKET ProvSocket;
    > Read[FD_SETSIZE], Wnte[FD_SETSIZE], Except[FD_SETSIZE];
    fd_set ReadFds, WriteFds, ExceptFds;
    // Построение набора ReadFds для нижестоящего поставщика
    if (readfds)
    {
    FD_ZERO(&ReadFds);
    for ( i = 0 ; i < readfds->fd_count; i++) ^ '
    7
    if (MainUpCallTable.lpWPUQuerySocketHandleContext(
    r
    *
    Й А
    '
    (Read[i].ClientSocket = readfds->fd_array[i]),
    (LPDWORD) &SocketContext, lpErrno) == ,'
    SOCKET_ERROR) , , ,
    return SOCKET_ERROR;
    FD_SET((Read[i].ProvSocket =
    SocketContext->ProviderSocket), &ReadFds);
    // Построение набора WriteFds для нижестоящего поставщика.
    // Это в точности подобно построению набора ReadFds выше.
    // Построение набора ExceptFds для нижестоящего поставщика.
    // Это также подобно построению набора ReadFds выше.
    // Вызов функции WSPSelect нижестоящего поставщика
    Ret = NextProcTable.lpWSPSelect(nfds,

    ГЛАВА 14 Интерфейс Winsock 2 SPI
    439
    Листинг 14-2. (продолжение)
    (readfds ? &ReadFds NULL),
    (writefds ? iWnteFds : NULL),
    (exceptfds ' &ExceptFds : NULL), timeout, lpErrno);
    if (Ret != SOCKET_ERROR)
    {
    HandleCount = Ret;
    // Настройка набора readfds вызывающего поставщика
    if (readfds)
    {
    count = readfds->fd_count;
    FD_ZERO(readfds);
    for(i =0; (l < count) && HandleCount; i++)
    <
    if (MamUpCallTable.lpWPUFDIsSet(
    Read[i].ProvSocket, &ReadFds))
    FD_SET(Read[i].ClientSocket, readfds);
    HandleCount-;
    // Настройка набора writefds вызывающего поставщика.
    // Это в точности подобно настройке набора readfds выше.
    // Настройка набора exceptfds вызывающего поставщика.
    // Это также подобно настройке набора readfds выше.
    return Ret;
    Модель WSAAsyncSelect
    Эта модель ввода-вывода описывает основанное на сообщениях Windows уведомление о сетевых событиях на сокете. SPI-клиенты используют ее, вы- зывая функцию WSPAsyncSelect:
    int WSPAsyncSelect(
    SOCKET s,
    HWND hWnd,
    unsigned int wMsg,
    long lEvent,
    LPINT lpErrno

    4 4 0 ЧАСТЬ И Интерфейс прикладного программирования Winsock
    Параметр s представляет сокет SPI-клиента, ожидающий сообщения Win- dows о сетевых событиях. Параметр hWnd задает описатель окна, которое дол- жно получить сообщение, определенное параметром wMsg, когда на сокете 5
    произойдет сетевое событие (определенное параметром lEvenf). Параметр
    ipErrno получает код ошибки Winsock, если реализация этой функции возвра- щает SOCKET_ERROR. Сетевые события, которые ваш поставщик должен под- держивать, заданы параметром lEvent (см. главу 8).
    Когда клиент вызывает WSPAsyncSelect, ваш поставщик отвечает за уведом- ление клиента о происходящих на сокете сетевых событиях, используя пре- доставленные клиентом описатель окна и сообщение (переданы через вы- зов WSPAsyncSelect). Ваш поставщик может уведомлять клиента о сетевых со- бытиях, вызывая функцию WPUPostMessage:
    BOOL WPUPostMessage(
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM IParam
    );
    Поставщик использует параметр hWnd, чтобы пометить как занятый опи- сатель окна клиента SPI. Параметр Msg определяет заданное пользователем сообщение, которое первоначально передано в параметр wMsg функции
    WSPAsyncSelect. Параметр WParam принимает описатель сокета, на котором произошло сетевое событие. Последний параметр — IParam, состоит из двух частей: нижняя содержит информацию о произошедшем сетевом событии,
    верхняя — код ошибки Winsock (если произошла ошибка, связанная с сете- вым событием, указанным в нижней части).
    Многоуровневый He-IFS-поставщик службы — клиент функции WSPAsync-
    Select нижестоящего поставщика. Значит, ваш поставщик должен транслиро- вать описатели сокета SPI-клиента в свои описатели сокета с использовани- ем WPUQuerySocketHandleContext перед вызовом функции WSPAsyncSelect ни- жестоящего поставщика. Чтобы сообщить в окне SPI-клиента о сетевых со- бытиях, поставщик должен использовать службы WPUPostMessage, и следова- тельно, перехватывать сообщения нижестоящего поставщика. Дело в том,
    что функция WPUPostMessage возвращает описатель сокета нижестоящего поставщика в верхнем слове параметра IParam, и ваш поставщик должен транслировать описатель сокета в верхнем слове IParam в описатель сокета
    SPI-клиента, который использовался в исходном вызове WSPAsyncSelect.
    Лучше всего управлять перехватом оконных сообщений нижестоящего поставщика с помощью рабочего потока, который создает скрытое окно для управления оконными сообщениями о сетевых событиях. Когда ваш постав- щик вызывает функцию WSPAsyncSelect нижестоящего поставщика, вы про- сто передаете описатель рабочего окна вызову функции WSPAsyncSelect ни- жестоящего поставщика. Теперь когда рабочее окно вашего поставщика по- лучит сообщения о сетевом событии от нижестоящего поставщика, ваш по- ставщик сообщит о них SPI-клиенту, используя WPUPostMessage.

    Г Л А В А 14 Интерфейс Winsock 2 SPI 441
    Модель ввода-вывода WSAEventSelect
    WSAEventSelect включает сигнальные объекты событий, когда на сокете про- исходят сетевые события. SPI-клиенты используют эту модель, вызывая функ- цию WSPEventSelect:
    int WSPEventSelect(
    SOCKET s,
    WSAEVENT hEventObject,
    long INetworkEvents,
    LPINT lpErrno
    );
    Параметр s — сокет SPI-клиента, который ожидает уведомления о сетевых событиях. Параметр hEventObject — описатель объекта WSAEVENT, о котором ваш поставщик сообщает, когда на сокете s происходят сетевые события,
    указанные в параметре INetworkEvents. Параметр lpErrno получает код ошиб- ки Winsock, если эта функция возвращает SOCKET_ERROR. Сетевые события,
    которые ваш поставщик должен поддержать (определенные параметром
    INetworkEvents) — те же, что и в модели ввода-вывода WSAAsyncSelect.
    В многоуровневом He-IFS-поставщике WSPEventSelect реализуется триви- ально. Когда SPI-клиент передает объект события, поставщик должен толь- ко транслировать описатель сокета SPI-клиента в описатель сокета нижесто- ящего поставщика, используя функцию WPUQuerySocketHandleContext. Пос- ле трансляции вызовите функцию WSPEventSelect нижестоящего поставщи- ка, используя объект события, взятый непосредственно от SPI-клиента. Ког- да операции ввода-вывода происходят на объекте события SPI-клиента, ни- жестоящий поставщик прямо сообщает об объекте события, и SPI-клиент уведомляется о сетевых событиях.
    Когда нижестоящий поставщик сообщает об объекте события, никакой описатель сокета не возвращается SPI-клиенту после завершения этого со- бытия. Поэтому ваш поставщик не должен транслировать описатель сокета для SPI-клиента. Этим модель ввода-вывода WSAEventSelect отличается от опи- санной ранее модели WSAAsyncSelect.
    При разработке базового поставщика, учтите, что он должен уведомлять SPI- клиента, когда на сокете происходят определенные параметром INetworkEvents
    сетевые события. При этом ваш поставщик использует предоставленный кли- ентом объект события, переданный в вызове WSPEventSelect. Ваш поставщик мо- жет уведомлять SPI-клиента о сетевых событиях через перекрестный вызов функции WPUSetEvent:
    BOOL WPUSetEvent (
    WSAEVENT hEvent,
    LPINT lpErrno
    );
    Параметр hEvent представляет переданный функцией WSPEventSelect опи- сатель объекта события, о котором ваш поставщик должен сообщить. Пара- метр lpErrno возвращает код ошибки Winsock, если функция возвращает
    FALSE.

    442 ЧАСТЬ II Интерфейс прикладного программирования Winsock
    Перекрытый ввод-вывод
    Модель перекрытого ввода-вывода требует, чтобы ваш поставщик реализовал диспетчер перекрытого ввода-вывода. Этот диспетчер будет обслуживать и со- бытия, основанные на объектах, и завершение основанных на процедурах зап- росов перекрытого ввода-вывода. Перекрытый ввод-вывод Win32 в Winsock используют SPI-функции WSPSend, WSPSendTo, WSPRecv, WSPRecvFrom, WSPIoctl.
    У каждой из этих функций есть три параметра: необязательный указатель на структуру WSAOVERLAPPED, необязательный указатель на функцию WSAO-
    VERLAPPED_COMPLETION_ROUTINE и указатель на структуру WSATHREADID -
    она определяет выполняющий вызов поток приложения.
    Структура WSAOVERLAPPED — ключевая для связи поставщика службы с SPI- клиентом в ходе операций перекрытого ввода-вывода. Она определена так:
    typedef struct .WSAOVERLAPPED {
    DWORD Internal;
    DWORD InternalHigh;
    DWORD Offset;
    DWORD OffsetHigh;
    WSAEVENT hEvent;
    } WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
    Диспетчер перекрытого ввода-вывода отвечает за управление полем Inter-
    nal структуры WSAOVERLAPPED для SPI-клиента. В начале перекрытой обра- ботки ваш поставщик службы должен присвоить полю Internal значение
    WSS_OPERATION_IN_PROGRESS. Это важно, ведь если SPI-клиент вызывает функцию WSPGetOverlappedResult, в то время как ваш поставщик обслуживает ожидающую выполнения перекрытую операцию, значение WSSOPERATI-
    ON_IN_PROGRESS может использоваться в WSPGetOverlappedResult для опре- деления, идет ли все еще перекрытая операция.
    Когда операция ввода-вывода закончена, ваш поставщик присваивает зна- чения полям OffsetHigh и Offset. Полю OffsetHigh присваивается значение кода ошибки Winsock, полученной в результате операции, а полю Offset — значе- ние флагов, полученных в результате операций ввода-вывода WSPRecv и
    WSPRecvFrom. После настройки этих полей ваш поставщик уведомит клиен- та SPI о завершении перекрытого запроса через объект события, либо через процедуру завершения (в зависимости от того, как функции ввода-вывода использовали необязательную структуру WSAOVERLAPPED').
    События. При основанном на событиях перекрытом вводе-выводе SPI- клиент вызывает одну из функций ввода-вывода со структурой WSAOVER-
    LAPPED, содержащей объект события Структуре WSAOVERLAPPED J0OMP-
    LETION_ROUTINE должно быть присвоено значение NULL. Ваш поставщик службы отвечает за управление запросом перекрытого ввода-вывода. Когда выполнение запроса завершается, поставщик уведомит поток вызывающей программы о завершении запроса ввода-вывода, используя перекрестный вызов функции WPUCompleteOverlappedRequest:
    WSAEVENT WPUCompleteOverlappedRequest(
    SOCKET s,

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


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