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

  • Табл. 8-1. Поддерживаемые модели ввода-вывода Платформа Windows СЕWindows 95 (Winsock 1)Windows 95 (Winsock 2)Windows 98Windows NTWindows 2000 Select

  • Листинг 8-2. Пример многопоточного программирования в режиме блокировки // Перед созданием двух потоков, // инициализируется общий буфер (data)

  • Листинг

  • // и сдвиг оставшихся в начало массива nBytes -= NUM_BYTES_REQUIRED, LeaveCnticalSection(data), }

  • Листинг 8-3. Создание сокета без блокировки

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


    Скачать 2.88 Mb.
    НазваниеЭ. Джонс, Д. Оланд
    АнкорПрограммирование в сетях Windows.pdf
    Дата12.10.2017
    Размер2.88 Mb.
    Формат файлаpdf
    Имя файлаПрограммирование в сетях Windows.pdf
    ТипКнига
    #9346
    страница20 из 50
    1   ...   16   17   18   19   20   21   22   23   ...   50
    Другие семейства адресов
    Все API-функции Winsock, представленные в этой главе, не зависят от про- токола Поэтому их легко можно применять и для других протоколов, под- держиваемых платформами Win32 Следующие разделы объясняют приме- ры клиент-серверного кода для других семейств протоколов, которые нахо- дятся на прилагаемом компакт-диске
    Протокол AppleTalk
    Единственный пример, связанный с AppleTalk, представлен для иллюстрации основных клиент-серверных технологий и поддерживает как РАР, так и ADSP
    Протокол РАР ориентирован на сообщения, не требует соединения, и не надежен Этим он похож на UDP, но есть и два важных отличия РАР поддер- живает фрагментарные сообщения, то есть функция WSARecvEx может вер- нуть лишь часть дейтаграммного сообщения При этом необходимо прове- рить значение флага MSG_PARTIAL, чтобы узнать, нужно ли дополнительно вызывать функцию для получения остатков сообщения Кроме того, необхо- димо задавать специфичные для протокола РАР параметры сокета перед каж- дым чтением (Подробнее о параметре SOJ4UME_READ, используемом совме- стно с функцией setsockopt, — в главе 9 ) Ознакомьтесь с примером A talk с на прилагаемом компакт-диске, где иллюстрируется проверка флага MSGPAR-
    TIAL и порядок использования параметра SOPRIMEREAD
    Протокол ADSP требует соединения, потоковый, надежный и во многом похож на TCP API-вызовы для AppleTalk практически не отличаются от пред- ставленных в этой главе примерах для UDP и TCP Разница лишь в разреше-

    2 0 2 ЧАСТЬ II Интерфейс прикладного программирования Winsock нии имен. Помните, что для AppleTalk перед поиском или регистрацией име- ни необходимо выполнить привязку к пустому адресу (см. главу 6).
    У протокола AppleTalk есть ограничение: его поддержка появилась в Win- sock 1.1, а когда был разработан Winsock 2, оказалось, что с AppleTalk не ра- ботают некоторые новые функции. Использование любой из WSASend- или
    WSARecv-функций может привести к непредсказуемым результатам, например к возврату отрицательного количества байт. Данная проблема изложена в ста- тье Q164565 базы знаний Microsoft Knowledge. Исключение составляет функ- ция WSARecvEx, которая просто вызывает recv, а в параметре ввода-вывода/я^
    можно просмотреть значение флага MSG'PARTIAL после выхода.
    Инфракрасные сокеты
    Поддержка IrDA появилась недавно, в Windows СЕ, 98 и 2000. Протокол IrDA
    требует соединения, потоковый и надежный. Снова вопрос в разрешении имен, которое значительно отличается от принятого в IP. Необходимо знать и еще одно отличие: на платформах Windows СЕ можно использовать толь- ко функции Winsock 1.1 для инфракрасных сокетов, так как Windows СЕ под- держивает только спецификацию Winsock 1.1. В Windows 98 и 2000 допус- тимо применение функций, специфичных для Winsock 2. Код примера ис- пользует только функции Winsock 1.1. В Windows 98 и 2000 необходимо заг- рузить библиотеку Winsock 2.2 или более позднюю, потому что поддержка семейства адресов AFJRDA в более ранних версиях не доступна.
    Пример кода для инфракрасных сокетов приведен в файлах Ircommon.h,
    Ircommon.c, Irclient.c и Irserver.c. Первые два файла просто определяют две общие функции: одну — для отправки, другую — для приема данных. Они используются как клиентским, так и серверным приложениями. Клиентская часть подробно расписана в файле Irclient.c. Сначала нумеруются все устрой- ства в зоне видимости. Затем следуют попытки подключиться к каждому из них с заданным именем службы. Дальнейшая работа ведется с первым уст- ройством, принявшим соединение. Клиент отправляет данные и считывает их обратно. Серверная часть описана в файле Irserver.c. Сервер создает ин- фракрасный сокет, выполняет привязку заданного имени службы к сокету и ожидает подключений клиентов. Для каждого клиента порождается поток для приема данных и отправки их обратно.
    Заметьте: данные примеры написаны для Windows 98 и 2000. Подобно примерам по TCP/IP, их нужно немного изменить для запуска в Windows СЕ.
    Также следует учесть два уже упоминавшихс ограничения Windows СЕ: эта
    ОС не поддерживает консольные приложения и требует кодировать строки в UNICODE.
    Интерфейс с NetBIOS
    В главе 1 мы говорили, что NetBIOS совместима с несколькими разными транс- портами, применяемыми в Winsock, а в главе 6 — обсудили, как регистрировать совместимые с NetBIOS транспорты и сокеты на основе любого из них. Для каждой комбинации «протокол — адаптер» есть две записи: SOCK_DGRAM и
    SOCK_SEQPACKET, соответствующих не требующим соединения дейтаграммам

    Г Л А В А 7 Основы Wmsock 203
    и потоковым сокетам, которые весьма похожи на сокеты UDP и TCP. Кроме раз- решения имен работа с Winsock-интерфейсом NetBIOS ничем не отличается от описанной в этой главе. Помните, что хорошо написанный сервер должен про- слушивать все доступные LANA, а клиент со своей стороны — пытаться устано- вить соединение по всем LANA.
    Первые два примера на компакт-диске — Wsnbsvr.c и Wsnbclnt с. Они ис- пользуют потоковые сокеты SOCKSEQPACKET. Сервер создает сокет для всех
    LANA, которые нумеруются функцией WSAEnumProtocols, и привязывает его к стандартному имени сервера. После установления клиентом соединения,
    сервер создает поток для его обслуживания. С этого момента поток просто читает входящие данные и возвращает их клиенту. Аналогично, клиент пы- тается подключиться по всем LANA. После первого успешного соединения другие сокеты закрываются. Затем клиент отправляет данные серверу, а сер- вер снова их возвращает.
    В файле Wsnbdgs.c приведен код для отправки и приема дейтаграммных сообщений (через сокет с типом SOCKJDGRAM). Этот протокол не требует соединения, поэтому для отправки сообщений серверу используются все доступные транспорты, так как заранее неизвестно, какие из них поддержи- вает сервер. Кроме того, в примере реализована поддержка уникальных,
    групповых и широковещательных сообщений (см. главу 1).
    Протокол IPX/SPX
    В файле Sockspx.c приведен пример использования протокола IPX, а также потокового и последовательного SPXII. В одном примере реализован отпра- витель и приемник для всех трех протоколов. Конкретный протокол зада- ется при помощи параметра -р в командной строке. Пример прост и легок в освоении. Функция main анализирует аргументы, а затем вызывает функ- цию Server или Client. Для протокола SPXII, ориентированного на соедине- ния, это значит, что сервер привязывает сокет к адресу внутренней сети и ожидает соединений с клиентом, который, в свою очередь, пытается устано- вить соединение с указанным в командной строке сервером. После установ- ления соединения отправка и прием данных идут обычным образом.
    Для не требующего соединения протокола IPX пример еще проще. Сер- вер просто выполняет привязку к внутренней сети и ожидает входящие дан- ные, вызывая функцию recvfrom. Клиент отправляет данные получателю, ука- занному в командной строке, функцией sendto.
    Два раздела этого примера требуют разъяснения. Функция FilllpxAddress
    отвечает за кодирование указанного в командной строке в кодировке ASCII
    IPX-адреса в структуру SOCKADDRJPX. Как упоминалось в главе б, IPX-адре- са представляются в виде шестнадцатеричных строк, то есть каждый символ адреса фактически занимает 4 бита в адресных полях структуры SOCKA-
    DDRJPX. Функция FilllpxAddress получает IPX-адрес и вызывает функцию
    AtoH, которая и выполняет преобразование.
    Еще одна функция — EnumerateAdapters, выполняется, если в командной строке задан флаг —т. Для подсчета количества доступных локальных IPX- адресов она использует параметр сокета IPX_MAX_ADAPTER_NUM, а затем для

    2 0 4 ЧАСТЬ II Интерфейс прикладного программирования Winsock получения каждого адреса вызывает параметр сокета IPXADDRESS. Мы ис- пользуем их, потому что в нашем примере IPX-адреса заданы напрямую, раз- решение имен не выполняется (подробнее — в главах 9 и 10).
    Протокол ATM
    Протокол ATM доступен в Winsock под управление Windows 98 и 2000. Пример для ATM состоит из файлов Wsockatm.c, Support.c, and Support.h. Два последних файла содержат вспомогательные подпрограммы, используемые в Wsockatm.c и обеспечивающие регистрацию локальных ATM-адресов и их перекодирова- ние. ATM-адреса шестнадцатеричные, как и IPX-адреса, поэтому мы применя- ем уже знакомую функцию AtoH. Для получения количества локальных АТМ- интерфейсов используется команда SIO_GET_NUMBERJDF_ATM_DEV1CES, а затем при помощи команды SIO_GET_ATM ADDRESS мы получаем фактический адрес
    (подробнее — в главе 9)-
    В отличие от описанных ранее примеров, клиентское и серверное при- ложение представлено одним файлом — Wsockatm.c. Так как ATM поддержи- вает только подключения, требующие соединения, пример небольшой и большинство кода находится в функции main. Сервер выполняет привязку к определенному локальному интерфейсу и ожидает подключений клиентов,
    которые обрабатываются в том же потоке, что и слушающий сокет. Это оз- начает, что сервер может одновременно обслуживать только одного клиен- та. Клиент вызывает connect с ATM-адресом сервера. После установления со- единения начинается обмен данными.
    А теперь несколько предостережений. После вызова сервером функции
    WSAAccept будет выведен адрес клиента. Однако к моменту получения серве- ром запроса на соединение адрес клиента еще неизвестен, потому что функ- ция accept не устанавливает соединение до конца. Это справедливо и для кли- ентской стороны — клиентский вызов для установления соединения с серве- ром признается успешным, даже если соединение до конца не установлено.
    Это означает, что после вызова connect или accept немедленная отправка мо- жет сорваться без выдачи предупреждений. К сожалению, приложение не спо- собно узнать, когда соединение полностью готово к использованию. Вдоба- вок, ATM поддерживает только резкое завершение: при вызове функции clo-
    sesocket соединение немедленно разрывается. Для протоколов, не поддержи- вающих плавное завершение, при вызове closesocket данные, ожидающие в очереди на любом конце соединения, обычно отбрасываются. Это вполне приемлемое поведение. Впрочем, поставщик ATM действует предусмотритель- но: при закрытии одной из сторон своего сокета во время передачи данных,
    Winsock все же возвращает данные из приемной очереди этого сокета.
    Резюме
    В этой главе обсуждались основные функции Winsock, необходимые для обмена данными по различным протоколам. Мы предоставили данную ин- формацию в контексте только одной модели ввода-вывода: блокирующих сокетов. В следующей главе мы рассмотрим другие модели ввода-вывода,
    доступные в Winsock.

    Г Л А В А
    Ввод-вывод в Winsock
    Эта глава посвящена управлению вводом-выводом через сокеты в Windows- приложениях. В Winsock такое управление реализовано с помощью режимов работы и моделей ввода-вывода. Режим (mode) сокета определяет поведение функций, работающих с сокетом. Модель (model) сокета описывает, как при- ложение производит ввод-вывод при работе с сокетом. Модели не зависят от режима работы и позволяют обходить их ограничения.
    Winsock поддерживает два режима: блокирующий (blocking) и неблоки-
    рующий (nonblocking). Эти режимы подробно описаны в начале главы, здесь же демонстрируется их использование в приложениях для управления вво- дом-выводом. Далее приведен ряд интересных моделей, которые помогают приложению управлять несколькими сокетами одновременно в асинхрон- ном режиме: select, WSAAsyncSelect, WSAEventS'elect, перекрытый ввод-вывод
    (overlapped I/O) и порт завершения (completion port). В конце главы рас- сматриваются достоинства и недостатки различных режимов и моделей, а также обсуждается выбор вариантов, наиболее подходящих для конкретных случаев.
    Все платформы Windows поддерживают блокирующий и неблокирую- щий режимы работы сокета. Впрочем, конкретная платформа может поддер- живать не все модели. В Windows СЕ доступна лишь одна модель ввода-вы- вода, в Windows 98 и Windows 95 — большинство моделей, кроме портов завершения (поддержка конкретной модели зависит от версии Winsock). В
    Windows NT и Windows 2000 доступны все модели (табл. 8-1).
    Табл. 8-1. Поддерживаемые модели ввода-вывода
    Платформа
    Windows СЕ
    Windows 95 (Winsock 1)
    Windows 95 (Winsock 2)
    Windows 98
    Windows NT
    Windows 2000
    Select
    Async
    Да
    Да
    Да
    Да
    Да
    Да
    WSA-
    Event
    Select
    Нет
    Да
    Да
    Да
    Да
    Да
    WS А -
    ввод-
    Select
    Нет
    Нет
    Да
    Да
    Да
    Да
    Перекрытый
    Port
    вывод
    Нет
    Нет
    Да
    Да
    Да
    Да
    Порт
    завершения
    Нет
    Нет
    Нет
    Нет
    Да
    Да

    2 0 6 ЧАСТЬ II Интерфейс прикладного программирования Winsock
    Режимы работы сокетов
    В блокирующем режиме функции ввода-вывода, такие как send и recv, перед завершением ожидают окончания операции. В неблокирующем — работа функций завершается немедленно. Приложения, выполняемые на платфор- мах Windows СЕ и Windows 95 (в случае Winsock 1), поддерживают очень мало моделей ввода-вывода и требуют от программиста описать блокирова- ние и разблокирование сокетов в разных ситуациях.
    Блокирующий режим
    При блокировке сокета необходима осторожность, так как этом режиме лю- бой вызов функции Winsock именно блокирует сокет на некоторое время.
    Большинство приложений Winsock следуют модели «поставщик — потреби- тель», в которой программа считывает или записывает определенное количе- ство байт и затем выполняет с ними какие-либо операции (листинг 8-1).
    Листинг 8-1. Простейший пример блокировки сокета
    SOCKET sock;
    char buff[256];
    int done = 0;
    while(ldone)
    nBytes = recv(sock, buff, 65);
    if (nBytes == S0CKET_ERR0R)
    printf("recv failed with error Xd\n"
    WSAGetLastErrorQ);
    Return;
    OoComputationOnData(buff);
    }
    Проблема в том, что функция recv может не завершиться никогда, так как для этого нужно считать какие-либо данные из буфера системы. В такой си- туации некоторые программисты могут соблазниться «подглядыванием»
    данных (чтение без удаления из буфера), используя флаг MSG_PEEK в recv
    или вызывая ioctlsocket с параметром FIONREAD. Подобный стиль програм- мирования заслуживает резкой критики. Издержки, связанные с «подгляды- ванием», велики, так как необходимо сделать один или более системных вы- зовов для определения числа доступных байт, после чего все равно прихо- дится вызывать recv для удаления данных из буфера.
    Чтобы этого избежать, следует предотвратить замораживание приложе- ния из-за недостатка данных (из-за сетевых проблем или проблем клиента)

    Г Л А В А 8 Ввод-вывод в Wmsock 207
    без постоянного «подглядывания» в системные сетевые буферы. Один из ме- тодов — разделить приложения на считывающий и вычисляющий потоки,
    совместно использующие общий буфер данных. Доступ к буферу регулиру- ется синхронизирующим объектом, таким как событие или мьютекс. 31да- ча считывающего потока — постоянно читать данные из сети и поме ч
    ать их в общий буфер. Считав минимально необходимое количество данных,
    этот поток инициирует сигнальное событие, уведомляющее вычисляющий поток, что можно начинать вычисления. Затем вычисляющий поток удаля- ет часть данных из буфера и производит с ними необходимые операции. В
    листинге 8-2 реализованы две функции: для приема данных (ReadThread) и их обработки (ProcessThread).
    Листинг 8-2. Пример многопоточного программирования в режиме
    блокировки
    // Перед созданием двух потоков,
    // инициализируется общий буфер (data)
    // и создается сигнальное событие (hEvent)
    CRITICAL.SECTION data;
    HANDLE hEvent;
    TCHAR buff[MAX_BUFFER_SIZE];
    int nbytes;
    // Считывающий поток
    void ReadThread(void)
    {
    int nTotal = 0,
    nRead = 0,
    nLeft = 0,
    nBytes = 0;
    while (!done)
    {
    nTotal = 0;
    nLeft = NUM_BYTES_REQUIRED;
    while (nTotal != NUM_BYTES_REQUIRED)
    {
    EnterCriticalSection(&data);
    nRead = recv(sock, &(buff[MAX_BUFFER_SIZE - nBytes]),
    nLeft);
    if (nRead == -1)
    {
    pnntf("error\n");
    ExitThreadO;
    nTotal += nRead;
    nLeft -= nRead;
    nBytes += nRead; Ы см.след.стр-

    2 0 8 ЧАСТЬ II Интерфейс прикладного программирования Winsock
    Листинг 8-2. (продолжение)
    LeaveCriticalSection(&data),
    >
    SetEvent(hEvent);
    // Вычисляющий поток
    void ProcessThread(void)
    {
    WaitForSingleObject(hEvent),
    EnterCriticalSection(&data),
    DoSomeComputationOnData(buff),
    // Удаление обработанных данных из буфера
    // и сдвиг оставшихся в начало массива
    nBytes -= NUM_BYTES_REQUIRED,
    LeaveCnticalSection(&data),
    }
    Недостаток блокировки сокетов — приложению трудно поддерживать связь по нескольким сокетам одновременно Приведенную схему можно из- менить, чтобы считывающий и вычисляющий потоки создавались отдельно для каждого сокета Для этого придется попотеть, зато вы получите более эффективное решение Но учтите данный вариант не предусматривает мас- штабирования, если вам придется работать с большим количеством сокетов
    Неблокирующий режим
    Альтернатива описанному режиму — режим без блокировки Он несколько сложнее в использовании, но обеспечивает те же возможности, что и режим блокировки, плюс некоторые преимущества В листинге 8-3 показано, как создать сокет и перевести его в неблокирующий режим
    Листинг 8-3. Создание сокета без блокировки
    SOCKET s,
    unsigned long ul = 1,
    int nRet,
    s = socket(AF_INET, SOCK_STREAM, 0);
    nRet = ioctlsocket(s, FIOBIO, (unsigned long *) &ul);
    if (nRet == SOCKET_ERROR)
    {
    1   ...   16   17   18   19   20   21   22   23   ...   50


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