Оглавление

    1. Протоколы обмена данными в ОС NetWare
    2. Система понятий и управляющие блоки протоколов
    3. Использование функций протоколов SPX и IPX
    3.1. Общая последовательность взаимодействия по протоколу IPX
    3.2. IPXInitialize
    3.3. IPXOpenSocket
    3.4. IPXGetInternetworkAddress
    3.5. IPXGetDataAddress
    3.6. IPXGetLocalTarget
    3.7. IPXSendPacket
    3.8. IPXListenForPacket
    3.9. IPXRelinquishControl
    3.10. IPXGetIntervalMaker
    3.11. IPXCancelEvent
    3.12. IPXDisconnectFromTarget
    3.13. IPXCloseSocket

1. Протоколы обмена данными в ОС NetWare

    При создании многопользовательских программных систем, функционирующих в среде локальных сетей, возникает необходимость организации обмена между процессами, функционирующими на различных узлах. Это может быть связано не обязательно с ведением разделяемых баз данных, но и с таким потребностями, как синхронизация процессов, организация работы с многопользовательским ресурсом и т.д.

    Для этого, исходя из возможностей ОС NetWare, можно выделить 3 способа коммуникации:

    1. Файловый.В этом случае процессы обмениваются информацией через файл, расположенный на сервере. Одна станция пишет в файл, другая (другие) читают информацию, помещенную туда. Этот метод прост в реализации, но не эффективен в случае его использования для синхронизации процессов и некоторых других приложений.

    2. С использованием сервисов NetWare.В сетевой операционной системе реализована поддержка механизмов синхронизации процессов на рабочих станциях (семафоры и логические записи), а также обмена информацией - Broadcast и Pipe сообщения. Доступ к ним можно получить через обращения к редиректору NetWare - NETx.COM(EXE). Программисты, пишушие на языке C, для этого могут использовать пакет фирмы Novell "NetWare C-Interface for DOS".

    3. С использованием функций низкоуровневых протоколов, на которых обменивается информацией сама сетевая операционная система. К числу этих протоколов можно отнести протоколы IPX и SPX. Эти протоколы могут позволить организовать связь между прикладными процессами на различных узлах сети наиболее эффективным образом с точки зрения скорости обмена и нагрузки на сетевую аппаратуру. Для того чтобы воспользоваться услугами протоколов IPX-SPX, совсем не обязательно наличие в локальной, сети файлового сервера и запуска программ редиректоров на рабочих станциях(NETx.COM). Эти протоколы поддерживаются программой-драйвером сетевой платы IPX.COM, либо пакетными драйверами.

    Программирование обмена с использованием низкоуровневых протоколов создает определенные трудности для программистов даже с достаточно высоким уровнем квалификации. Проблемы здесь связаны с точным пониманием логики работы персонального компьютера, а также с явно недостаточно полным описанием функций работы с протоколами, предоставленным фирмой Novell с пакетом "NetWare C-Interface for DOS".

    Протокол IPX(Internetwork Packet Exchange - Межсетевой Обмен Пакетов) принадлежит к классу так называемых датаграммных протоколов транспортного уровня, т.е. "послал-забыл". То есть он не дает гарантий, что пакет был правильно принят адресатом. Но это не означает, что пакет может быть утерян на пути от источника к адресату аппаратурой сети. Если адрес указан верно, и работающая станция-адресат включена, то пакет с вероятностью, равной надежности аппаратуры сети, будет ею получен в буфер сетевой платы. По собственному опыту работы авторы использовали этот протокол для реализации обмена в системе проведения электронного аукциона и программе сетевой пересылки файлов, фрагменты которой приведены в качестве примера.

    Протокол SPX(Sequenced Packet Exchange - Последовательный обмен пакетов) можно отнести к классу протоколов виртуального соединения или гарантированной доставки. Посылающий процесс получает подтверждение о получении пакета принимающей стороной автоматически. Это во многом упрощает программирование систем, в которых четко требуется реализация механизма подтверждения о приеме пакета. В принципе протокол SPX является протоколом более высокого уровня, чем IPX,поскольку он полностью реализован на его примитивах. Схема протокола SPX заведомо исключает получение соединений типа один со многими, т.е. процесс может установить обмен через одно соединение только с одним процессом в другом узле.

    На протоколе SPX реализована сама ОС NetWare и все сопровождающие ее многочисленные продукты фирмы Novell - серверы печати, коммуникационные серверы, шлюзы и т.д.

    В настоящем издании, во многом опираясь на руководство к пакету "NetWare C-Interface for DOS", мы попытались изложить описание коммуникационного сервиса NetWare в несколько иной - более практической транскрипции, попытавшись уменьшить трудность его первоначального понимания с помощью более развитой системы примеров.

2. Система понятий и управляющие блоки протоколов

    Тем, кто не занимался серьезно программированием с использованием протоколов транспортного уровня, первоначально достаточно сложно уяснить некоторые понятия, используемые в документации к пакету "NetWare C-Interface for DOS". Важнейшим из них является понятие сетевого адреса.

    В протоколах IPX-SPX сетевой адрес состоит из трех полей, Первое поле (4 байта) содержит номер сети. Он задается при генерации файлового сервера или межсетевого моста. Если в локальной сети сервер (мост) выключен или отсутствует, то в этом поле должны стоять нули. Следующее поле (6 байт) содержит адрес сетевой платы станции. В платах ArcNet этот адрес задается вручную на этапе сборки сети, в Ethernet он, как правило, прожигается на заводе. Последнее поле (2 байта) содержит номер сокета (англ.socket-гнездо, розетка). Оно используется для задания адреса процесса внутри узла. В "NetWare C-Interface for DOS" он описан ввиде struct IPXAddress в файле NXT.H :

      BYTE   network[4];                  /* старший-младший */
      BYTE   node[6];                     /* старший-младший */
      WORD   socket;                      /* старший-младший */

    "Старший-младший" определяет порядок следования байтов. Нормальный порядок следования байт "младший-старший", поэтому при работе с этими полями нужно быть особенно внимательным и для извлечения информации из них необходимо использовать функции типа IntSwap().

    Типы BYTE и WORD определены в NXT.H соответственно как unsigned char и unsigned int.

    После получения сигнала от сетевой платы о получении или успешной отсылки пакета, IPX.COM вызывает связанную с этим гнездом пользовательскую программу обработки события. И уже после этого данные могут быть обработаны прикладной программой.

    Под событием понимают одноразовую операцию, связанную с приемом (отсылкой) одного пакета. Все события группируются вокруг тиков таймера компьютера (если не использовать IPXRelinqushControl см. ниже).

    Взамодействие прикладной программы с IPX.COM происходит через блок обработки события (Event Control Block), сокращенно "ECB". Он представляет непрерывный участок памяти длиной 48 байтов. В терминах "NetWare C-Interface for DOS" это struct ECB. Она определена в файле NXT.H и состоит из следующих полей:

    void            far *linkAddress;
    void            (far *ESRAddress)();     /* sr t */
    BYTE            inUseFlag;
    BYTE            completionCode;
    WORD            socketNumber;            /* sr,старший-младший */
    BYTE            IPXWorkspace[4];         /* N/A */
    BYTE            driverWorkspace[12];     /* N/A */
    BYTE            immediateAddress[6];     /* s, старший-младший */
    WORD            fragmentCount;           /* sr, */
    ECBFragment     fragmentDescriptor[2];

    В приведенных выше комментариях, символ "s" (Send) обозначает поля, которые должна установить прикладная программа при использовании Блока ECB для отправки пакета. Буква "r" (Receive) обозначает поля, которые должна установить прикладная программа при использовании Блока ECB для получения пакета. Символ "t" (Time) обозначает поля, которые должна установить прикладная программа при использовании Блока ECB для планирования события по часам (во времени). "Старший-младший" определяет порядок следования байтов. Нормальный порядок следования байт "младший-старший", поэтому при работе с этими полями нужно быть особенно внимательным и для извлечения информации из них необходимо использовать функции типа IntSwap().

    Протокол IPX-SPX использует полe linkAddress для внутренних целей.

    Поле ESRAddress (адрес ESR - Event SubRoutine) содержит адрес определяемой прикладной программой Подпрограммы. При вызове этой программы регистры ES:SI содержат адрес вызвавшего ее блока обработки события. Эту подпрограмму нужно писать по всем правилам написания программы обработки прерывания - восстанавливать сегмент данных программы, аккуратно работать с функциями, использующими обращения к DOS. Например, использование функции printf() в данной процедуре приводит к зависанию.

    IPX-SPX записывает в поле inUseFlag (флаг использования) текущее состояние события. Прикладная программа может задействовать это поле для определения факта завершения события.

    Поле completionCode (код завершения) содержит код завершения для события, который обозначает, был ли пакет отправлен успешно, или пакет имеет неправильный формат и т.п.

    Поле socketNumber (номер гнезда) определяет отправляющий или получающий сокет, к которому подстыковывается данный ECB.

    Поля IPXWorkspace (рабочая область IPX) и driverWorkspace (рабочая область драйвера) используются для внутренних целей протоколом и драйвером сетевой платы. Эти поля нельзя изменять. Протокол SPX возвращает в этом поле идентификационный номер соединения SPX.

    Поле immediateAddress (непосредственный адрес) содержит адрес узла, которому отправляется пакет, или узла, от которого он получается. Если пакет не получен от узла и не отправлен узлу в местной сети, то этим адресом будет адрес межсетевого моста в местной сети. Для заполнения этого поля следует использовать функцию IPXGetLocalTarget.

    Поле fragmentCount обозначает количество буферов (не более двух), из которых будет построен исходящий (отправляемый) пакет, или на которые будет разбит принимаемый пакет. Прикладная программа дает список дескрипторов фрагментов в конце Блока ECB, с буфером адреса и размера, обозначенным посредством поля fragmentCount. Первый фрагмент обязательно в начале должен содержать место под протокольный заголовок пакета (struct IPXHeader или struct SPXHeader). Суммарная длина двух фрагментов не должна превышать 576 байтов.

    Заголовок пакета IPX - IPXHeader определен в файле NXT.H и состоит из следующих полей:

             WORD        checkSum;         /* старший-младший */
             WORD        length;           /* старший-младший */
             BYTE        transportControl;
             BYTE        packetType;
             IPXAddress  destination;
             IPXAddress  source;

    CheckSum - предназначено для контрольной суммы содержимого пакета и всегда устанавливается IPX как 0xFFFF. Прикладной прог- рамме данное поле устанавливать не надо.     Length - содержит длину полного пакета IPX (от 30 до 576 байт) состоящего из заголовка пакета IPX ( 30 байт) и порции данных (от 0 до 546 байт). IPX.COM сам задает значение этого поля как FragmentDescriptor[0].Size + FragmentDescriptor[2].Size при отправке пакета. Обратите внимание на "старший-младший" - длина принятого пакета = IntSwap(Length).

    transportControl - используется межсетевыми мостами NetWare для определения количества мостов или маршрутов, которые прошел пакет. Далее 16-го моста пакеты не проходят. IPX устанавливает это поле как ноль перед тем, как отправлять пакет.

    packetType - определяет тип пакета. Значительная часть типов используется Novell для своих внутренних нужд и рекомендует программистам устанавливать его значение в 0 или 4. Для пакета SPX IPX.COM в это автоматически прописывает 5.

    destination - содержит сетевой адрес станции получателя и номер сокета на ней и имеет тип IPXAddress. При отправке пакета прикладная программа должна установить это поле. Для передачи широковещательного сообщения в байты соответствующие номеру узла необходимо записать 0xFFFFFFFFFFFF.

   &nbspsource - в это поле IPX.COM заполняет сетевой адрес узла, отсылающего пакет и номер сокета.

    Протокол SPX имеет семь дополнительных полей в своем заголовке. Его длинна 42 байта. Он определен в NXT.H как struct SPXHeader

        
        WORD checkSum;
        WORD length;                /* старший-младший  */
        BYTE transportControl;
        BYTE packetType;
        IPXAddress  destination;
        IPXAddress  source;
        BYTE connectionControl;     /* флаги бит */
        BYTE dataStreamType;
        WORD sourceConnectionID;    /* старший-младший  */
        WORD destConnectionID;      /* старший-младший  */
        WORD sequenceNumber;        /* старший-младший  */
        WORD acknowledgeNumber;     /* старший-младший  */
        WORD allocationNumber;      /* старший-младший  */

    Протокол SPX устанавливает все поля в заголовке SPX в IPX.COM, кроме поля destination и dataStreamType.

    Поле connectionControl управляет двунаправленным потоком данных по соединению SPX.

    Поле dataStreamType обозначает тип данных, включенных в пакет. Величины с (0x00) до (0xFD) определяются пользователем и игнорируются протоколом SPX. Величина (0xFE) обозначает пакет конца соединения. Когда пользователь делает вызов SPXTerminateConnection(), SPX генерирует пакет 0xFE. Этот пакет затем отправляется партнеру по соединению как последний пакет в соединении. Величина (0xFF) обозначает квитированный пакет конца соединения. SPX генерирует пакет End-of-Connection-Acknowledgement (Конец-Соединения-Квитирование) автоматически. Он помечается как системный пакет и не доставляется пользователям-партнерам. Поэтому величины (0xFE) и (0xFF) не следует использовать.

    Поля sourceConectionID и destConectionID определяют номер соединения, который SPX присваивает соединению отправляющим узлом и узлом-адресатом, соответственно.

    Поле sequenceNumber содержит количество пакетов, отправленных в одном направлении по соединению. Каждая сторона соединения ведет свой собственный подсчет.

    Поле acknowledgeNumber хранит номер следующего пакета, который ожидает получить соединение SPX.

    Поле allocationNumber обозначает число свободных блоков управления событием узла-партнера для приема пакетов. SPX будет отправлять пакеты, пока локальное sequenceNumber не станет равно allocationNumber удаленного партнера.

    Величины в трех последних полях пошагово увеличиваются от 0000h до FFFFh, а затем снова переводятся на нули - поля являются счетчиками по модулю 0xFFFF+1.

3. Использование функций протоколов SPX и IPX.

    В описании функций из раздела "Коммуникационные услуги" пакета "NetWare C-Interface for DOS" их разбили на три группы:

    Реально для написания программы на IPX-SPX могут потребоваться вызовы функций всех трех групп. Поэтому мы отказались от стандартного способа изложения их описания и предлагаем здесь описание функций по способам их использования в том порядке в каком они реально могут встретится в прикладной программе использующей протоколы IPX-SPX.

    За исключением IPXSheduleIPXEvent, вам будут необходимы для написания программы обмена по IPX все функции асинхронного планировщика событий. Чтобы далее к ней не возвращаться, скажем, что IPXSheduleIPXEvent не используется сама по себе при пересылке информации. Она может быть использована как способ унификации обработки событий в вашей программе.

3.1. Общая последовательность взаимодействия по протоколу IPX

    В самом начале работы прикладная программа должна проверить наличие IPX в оперативной памяти и получить адрес входа для интерфейса IPX, вызвав функцию IPXInitialize. Данная функция должна быть вызвана до вызова любой функции IPX.

    Перед тем, как начать обмен пакетами с помощью IPX, прикладные программы на обеих сетевых станциях должны открыть сокеты функцией IPXOpenSocket. Номер сокета, открытого на станции А должен быть известен прикладной программе на станции Б и наоборот.

    Номера сокетов определить просто - достаточно установить соглашение между прикладными программами по использованию их номеров гнезд.

    Обе станции должны знать сетевой адрес (адрес сети и узла) друг друга. Но автоматически его определить невозможно. Как правило известен только пользовательский идентификатор сетевой станции. Для получения по нему сетевого адреса другой станции можно воспользоваться функциями GetObjectConnectionNumbers (см. Connection Service NetWare C-Interface for DOS) или IPXGetInternetworkAddress, вызвав их в начале программы. Но необходимо помнить, что NetWare позволяет использовать один и тот же идентификатор пользователя на нескольких станциях одновременно. В этом случае обычно используется первое значение из полученного списка. Остальные значения игнорируются.

    По окончании передачи и приема пакетов, прикладные программы на обеих станциях должны закрыть гнезда, вызвав функцию IPXCloseSocket.

    После того, как прикладная программа на станции А открыла гнездо и определила адрес сети, узла и номер гнезда на станции Б, а станция Б получила такую же информацию о станции А, они готовы к приему и передаче пакетов. Для отправки пакета используется функция IPXSendPacket, а для приема - IPXListenForPacket. При каждом вызове IPXSendPacket ей должно передаваться дополнительное поле - immediateAddress.

    Поле immediateAddress содержит адрес узла, которому отправляется пакет, или узла, от которого он получается. Если пакет не получен от узла и не отправлен узлу в местной сети, то этим адресом будет адрес межсетевого моста в местной сети. Для получения информации для этого поля может быть использована функция IPXGetLocalTarget.

    IPX не переходит в состояние ожидания окончания отправки или получения пакета, эти операции только инициализируются. Реальная отправка и прием происходят в фоновом режиме. Ход этих операций можно отслеживать двумя способами: либо прикладная программа периодически проверяет признак завершения операции inUseFlag, либо передает IPX адрес подпрограммы (ESRAddress), которая будет выполнена при завершении события.

3.2. IPXInitialize

       BYTE IPXInitialize(void)

    Данная функция обращаясь к мультиплексному прерыванию с кодом 0x7A детектирует наличие IPX.COM в памяти и получает адрес точки входа в IPX (IPXLocation). IPXInitialize() обязательно должна присутствовать перед первым вызовом любой другой функции. В случае отсутствия IPX.COM в памяти (на бридже или файл-сервере это будет протокол, связанный с драйвером сетевой платы) функция возвращает значение 240 (0xF0).

    Ее можно использовать следующим образом:

      #include     

        if (IPXInitialize())
                error("Not louded IPX/SPX",1);

    Все ниже перечисленные функции, за исключением IPXGetDataAddress, являются непосредственно функциями протокола IPX-SPX.

    Обращения к ним формируются как вызов функции (*IPXLocation)() с соответствующим кодом :

    IPXOpenSocket                0x00
    IPXCloseSocket               0x01
    IPXGetLocalTarget            0x02
    IPXSendPacket                0x03
    IPXListenForPacket           0x04
    IPXSheduleEvent              0x05
    IPXCancelEvent               0x06
    IPXGetIntervalMaker          0x08
    IPXGetInternetworkAddress    0x09
    IPXRelinquishControl         0x0A
    IPXDisconnectFromTarget      0x0B
    SPXInitialize                0x10
    SPXEstablishConnection       0x11
    SPXListenForConnection       0x12
    SPXTerminateConnection       0x13
    SPXAbortConection            0x14
    SPXGetConnectionStatus       0x15
    SPXSendSequencedPacket       0x16
    SPXListenForSequencedPacket  0x17

3.3. IPXOpenSocket

    Данная функция открывает сокет(гнездо IPX).

      int IPXOpenSocket(BYTE *socketNum, BYTE socketType)
        
        Возвращаемые значения:

    0 (0x00)   SUCCESSFUL           Успешное выполнение
    240 (0xF0) IPX_NOT_INSTALLED    IPX не установлен
    254 (0xFE) SOCKET_TABLE_FULL    Таблица сокетов заполнена
    245 (0xFF) SOCKET_ALREADY_OPEN  Сокет уже открыт

    Прикладная программа должна использовать эту функцию для открытия сокета перед тем, как получить пакет.

    Параметр socketNumber содержит номер открываемого сокета. Передача величины 0000h в это поле позволяет IPX открыть доступные гнезда по своему выбору в диапазоне от 0x4000 до 0x5000. Это называется ДИНАМИЧЕСКИМ открытием гнезда.

    Если данная функция возвращает код завершения (0x00), то гнездо было успешно открыто, как и ожидалось. Если код завершения равен (0xFE), то таблица сокетов уже заполнена.

    Если функция возвращает код завершения (0xFF), то указанный сокет уже открыт.

    Параметр socketType определяет, как долго сокет должен оставаться открытым. Если только программа не собирается завершаться и оставаться резидентной, тип гнезда может быть временным - "краткосрочным" (0x00). Гнезда, открытые как временные (краткосрочные) - SHORT_LIVED - автоматически закрываются при завершении программы (если загружен редиректор NetWare NETx.COM) и все соответственные события, ожидающие на этом гнезде, отменяются. Если прикладная программа открывает гнездо как постоянное ("долгосрочное") - LONG_LIVED - (0xFF), она должна гарантировать, что подпрограмма ESR (подпрограмма обслуживания события) будет доступной даже после завершения этой прикладной программы. В противном случае рабочая станция может зависнуть, если подпрограмма ESR (подпрограмма обслуживания события) вызывается IPX после того, как она стала недоступной.

    По умолчанию, IPX поддерживает до 20 открытых гнезд на рабочей станции. Максимальное количество одновременно открытых гнезд для рабочей станции может достигать 150; конфигурация производится в файле SHELL.CFG.

        Пример:

     #include     
     int s_open=0;
     OpenSocket( int SocketNumber)
     {
          int rc ;
          rc=IPXOpenSocket((BYTE*)&SocketNumber,0);
          switch (rc){
              case  0xFE :
              error("Socket table full. Programm aborted.", rc);
              break;
              case  0xFF :
              printf("\n\rSocket %X has been  already
               opened.\n\r",SocketNumber);
              }
          s_open++;
     }

3.4. IPXGetInternetworkAddress

    Данная функция возвращает адрес сети и узла запрашивающей рабочей станции.

    void IPXGetInternetworkAddress(BYTE *networkAddress)

    Параметр networkAddress заполняется 10-байтовым адресом узла в сети. Байты 0-3 = номер сети Байты 4-9 = номер узла

    Пример:

             #include     
             IPXAddress   networkAddress;
             IPXGetInternetworkAddress(networkAddress);
             printf("\nMy network address is");
             for (i=0;i<3;i++);
               printf("%X ",networkAddress[i]);
             for (;i<10;i++);
               printf(" %X",networkAddress[i]);

3.5. IPXGetDataAddress

    Данная функция возвращает адрес данных в буфере, на который указывает параметр Address.

     void IPXGetDataAddress(char *data, WORD *address)

    Параметр data является указателем на элемент данных, чей адрес будет возвращен в параметре address. Адрес возвращается в порядке сегмент-смещение. (То есть, адрес = смещение, адрес + 2 = сегмент).

    Эта функция не являет собой обращение к какой либо функции протокола IPX-SPX. Она главным образом необходима для того чтобы не путаться в вычислении адреса буффера данных в различных моделях памяти компиляторов языка Си.

    Пример:

  #include     
  #include     
  char         data[530];
  ECB ecb1;
  IPXGetDataAddress(data,(WORD*)&ecb1.fragmentDescriptor[1].address);
  /* Это эквивалентно для TURBO C  small model */
  ecb1.fragmentDescriptor[1].address =(void* far) data;

3.6. IPXGetLocalTarget

    Данная функция необходима при установлении связи по протоколу IPX, особенно в том случае, когда связываемые узлы находятся в разных сегментах сети. Она помещает в поле immediateAddress (непосредственный адрес) блока ECB адрес ближайшего узла, до которого будет идти пересылка. Если узлы находятся в одном сегменте сети, то это будет непосредственный узел адресата, в противном случае это будет адрес сетевого бриджа (файлового сервера).

             int IPXGetLocalTarget(BYTE *networkAddress,
                 BYTE *immediateAddress, int *transportTime)
    Параметр networkAddress содержит указатель на 12-байтовый адрес получателя в сети.

    Параметр immediateAddress получает указатель на 6-байтовое поле блока ECB.

    Параметр transportTime получает подсчет времени (в шагах таймера системы, равных приблизительно 1/18 части секунды), которое занимает передача 576-байтового пакета от исходного узла к узлу-адресату. Так как возвращаемая величина является только приближенным подсчетом, фактическое время передачи может отличаться от данного, в зависимости от трафика сети и размера пакета. Если у вас отправитель и адресат связаны скоростными каналами локальных сетей, это значение как правило равно 1. Если же между ними лежит асинхронный бридж с телефонными линями связи, вы можете через это поле определять значения таймаутов в вашей программе.

    В случае успешного нахождения сети получателя функция выполняется достаточно быстро и возвращает значение 0 (0x00) SUCCESSFUL. В противном случае она как бы на время подвешивает машину и возвращает 250 (0xFA) NO_LOCAL_TARGET_IDENTIFIED . Данная функция передает IPX межсетевой адрес узла-получателя. IPX возвращает вычисленное время передачи пакета и либо адрес узла для узла-адресата, либо адрес узла для локального моста, который будет использовать IPX для маршрутизации пакета к узлу-адресату.

    Функция IPXGetLocalTarget может быть использована с адресами получателей используя широковещательные величины (все 0xFF) в поле node (узел).

    Когда IPX получает пакет, он записывает адрес узла - отправителя в поле immediateAddress получающего блока ECB. Шесть байт immediateAddress - это номер локального узла-отправителя, а не номер исходного узла. Номер локального узла-отправителя - это номер вызывающей рабочей станции. Следовательно, когда прикладная программа начинает обмен пакетами с прикладной программой на другом узле, любая из этих прикладных программ может использовать поле immediateAddress из получающего блока ECB для извлечения номера местного узла-адресата, вместо того, чтобы повторно вызывать функцию IPXGetLocalTarget.

    а) Отправитель и Получатель находятся в разных сегментах сети.

    Результатом работы IPXGetLocalTarget на станции "Отправитель" будет адрес immediateAddress_1.

    На станции "Получатель" после приема пакета поле immediateAddress в ECB будет содержать адрес immediateAddress_N.

    б) Отправитель и Получатель находятся в одном сегменте сети. immediateAddress совпадает с networkAddress.

    Пример :

     
     IPXAddress netAddr;
     int timeout;
     ECB ecb1 ;
     if( IPXGetLocalTarget(netAddr,ecb1.immediateAddress,&timeout)
          error("Ошибка адресации",1);
     if memcmp(&netAddr[4],ecb1.immediateAddress,6)
         printf(" Получатель и мы в одном сегменте сети ") ;
     else
         printf(" Получатель и мы в разных сегментах сети ") ;

3.7. IPXSendPacket

    Данная функция инициализирует отправку пакета IPX.

     void IPXSendPacket(ECB *eventControlBlock)

    Функция IPXSendPacket передает IPX адрес блока ECB для отправки пакета IPX. Эта функция возвращает управление вызывающей прикладной программе, не дожидаясь окончания процесса отправки. С этого момента IPX пытается отправить пакет. Прикладная программа может продолжить свою работу, проверяя окончание процесса отправки либо по значению поля inUseFlag в ECB, либо ожидая вызова процедуры, адрес которой должен быть указан в поле ESRAddress в ECB.

    Перед тем, как вызвать эту функцию, прикладная программа должна инициализировать следующие поля блока ECB :

    Если с данным ECB связывается некоторая процедура обработки события, то ее адрес должен быть занесен в поле ESRAddress (адрес подпрограммы ESR). Если никакой подпрограммы не вызывается, поле ESRAddress должно содержать NULL.

    Прикладная программа должна также подготовить заголовок IPX соответствующего пакета посредством заполнения полей packetType и destination (адресат). Затем функция передает блок ECB драйверам коммуникации сети для начала операции отправки.

    Несмотря на то, что socketNumber в Блоке ECB имеет значение (IPX использует его как номер исходного узла в заголовке пакета), гнездо НЕ НУЖНО открывать для выполнения операции отправки.

    Поле immediateAddress в Блоке ECB может быть установлено при помощи функции IPXGetLocalTarget.     Сначала IPX устанавливает поле блока ECB inUseFlag как (0xFF), - это обозначает, что блок ECB отправляет пакет. После попытки отправить пакет, IPX устанавливает поле completionCode блока ECB до соответствующей величины и устанавливает поле inUseFlag как (0x00) (AVAILIABLE_FOR_USE, и вызывает подпрограмму ESR (подпрограмму обслуживания события), на которую указывает поле ESRAddress.     Ниже приводятся возможные величины для поля completionCode блока ECB:

     0 (0x00)   SUCCESSFUL        Успешное выполнение
     252 (0xFC) REQUEST_CANCELLED Запрос отменен
     253 (0xFD) BAD_PACKET        Неправильный пакет
                                  (Данный пакет не
                                   имеет  30-байтового
                                   заголовка пакета в качестве
                                   первого фрагмента, или же
                                   общая длина пакета превышает
                                   576 байт)
     254 (0xFE) PACKET_NOT_DELIVERABLE  Пакет  не  может быть
                                   доставлен
     255 (0xFF) HARDWARE_FAILURE   Сбой в аппаратуре

    Более подробную информацию по кодам возврата вы можете получить из документации на пакет программ "NetWare C-Interface for DOS"

    Код завершения (0x00) обозначает, что пакет был успешно отправлен, но не гарантирует, что он был успешно получен узлом-адресатом. Например, носитель может потерять или повредить (засорить) пакет, или же сокет-адресат может оказаться закрытым или не ждущим сигнала (не "слушающим"). IPX не сообщает ничего отправляющему узлу, если возникают подобные проблемы.

    Код завершения (0xFE) обозначает, что пакет не может быть доставлен. Блок ECB возвращает этот код завершения в одном из двух случаев:

    Прикладная процесс может посылать или широковещетельно отправлять пакет IPX в любой узел в интерсети, включая узел-отправитель. Пакеты, отправленные к сокетам, которые находятся в том же узле, что и сам прикладной процесс, называются "внутриузловыми" пакетами.

    Пример:

             #include     
             ECB          ecb1;
             IPXHeader    ipxh;
             ..............
             IPXInitialize
             ...

             IPXOpenSocket
             ...
             IPXGetLocalTaget
             .................
             memcpy(ipxh.destination, ,  )
             ecb1.FragmentDescriptor[0].adress=(void far*) ipxh;
             .......................
             IPXSendPacket (&ecb1);
             while (ecb1.inUseFlag);
             if (!ecb1.completionCode)
                 printf("Пакет успешно отослан");

3.8. IPXListenForPacket

    Данная функция готовит IPX к получению пакета IPX.

             void IPXListenForPacket(ECB *eventControlBlock)

    Функция IPXListenForPacket передает IPX адрес блока ECB для получения пакета IPX. Эта функция затем возвращает управление вызывающей прикладной программе. В это время IPX ожидает сигнала ("слушает") и пытается получить пакет.

    Перед тем, как вызвать эту функцию, прикладная программа должна открыть гнездо и инициализировать поля блока ECB ESRAddress, socketNumber, immediateAddress, fragmentCount и fragmentDescriptor. Если никакой подпрограммы не вызывается, поле ESRAddress должно содержать NULL.

    Сначала IPX устанавливает поле Блока ECB inUseFlag как (0xFF), - это обозначает, что Блок ECB (Блок контроля события) ожидает получения пакета. IPX также прибавляет Блок ECB к буферной области (пулу) Блоков ECB, ждущих сигнала от пакетов IPX в одном и том же сокете. IPX не налагает ограничений на количество Блоков ECB, которые могут параллельно ждать сигнала в одном сокете.

    Когда IPX обнаруживает прибывающий пакет, он использует один из ожидающих сигнала Блоков ECB для получения этого пакета. Ожидающие сигнала Блоки ECB НЕ заполняются в том порядке, в каком они поступают в IPX. IPX записывает соответствующую величину в поле completionCode (код завершения) выбранного Блока ECB и помещает адрес узла в поле immediateAddress Блока ECB. Эта величина в поле immediateAddress обозначает либо отправляющий узел (если отправляющий узел находится в том же сегменте сети), либо локальный мост, который переправил пакет (если отправляющий узел не находится в локальной сети).

    Наконец, IPX устанавливает поле inUseFlag (флаг использования) блока ECB как 00h (AVAILIABLE_FOR_USE - Доступен для использования), и вызывает подпрограмму ESR (подпрограмму обслуживания события), на которую указывает поле ESRAddress (адрес подпрограммы ESR) Блока ECB (если это возможно).

    Ниже приводятся возможные величины для кода завершения Блока ECB (Блока контроля события):

     0 (0x00) SUCCESSFUL          Успешное выполнение
     252 (0xFC) REQUEST_CANCELLED Запрос отменен
     253 (0xFD) BAD_PACKET        Неправильный пакет
     255 (0xFF) SOCKET_NOT_OPEN   Гнездо не открыто


             #include     
             ECB          eventControlBlock;
             IPXListenForPacket (&eventControlBlock);

    Пример:

             #include     
             ECB          ecb1;
             IPXHeader    ipxh;
             int timeout;
             ..............
             IPXInitialize
             ...
             IPXOpenSocket
             ...
             IPXGetLocalTaget(,,&timeout)
             .................
             memcpy(ipxh.destination, ,  )
             ecb1.FragmentDescriptor[0].adress=(void far*) ipxh;
             .......................
             IPXListenForPacket (&ecb1);
             for (;;)
             swtich (getpacket(&ecb1,timeout+1)) {
             case TIMEOUT
             ..............
             case USER_BREAK
             ..............
             }
             ....
             int getpacket(ECB* ecb, int timewait)
              {
               int tims;
                    if (!ecb.inUseFlag)
                         IPXListenForPacket(&ecb);

                    tims=IPXGetIntervalMarker()+timewait;
                    do  {
                         IPXRelinquishControl();
                         if (!ecb.inUseFlag) {
                              return ecb.completionCode;
                         }
                         if ((kbhit())&&(getch()==27))
                              return USER_BREAK;
                    } while (tims > IPXGetIntervalMarker());
                    return TIMEOUT;
               }

3.9. IPXRelinquishControl

    Данная функция передает в IPX управление центральным процессором рабочей станции.

     void IPXRelinquishControl(void);

    Прикладная программа может использовать данную функцию для временной передачи управления в IPX, чтобы во то время, когда она, например, ожидает ввода с клавиатуры, могла выполняться обработка принятых пакетов. Иначе запуск коммуникационного драйвера IPX будет происходить на очередном тике внутреннего таймера машины.

    Пример ее использования смотрите в примере к функции IPXListenForSocket.

3.10. IPXGetIntervalMaker

    Данная функция получает от IPX значение временного маркера.

    unsigned IPXGetIntervalMaker(void)

    Маркер интервала является величиной в диапазоне от 0 до 65535 (0x0000 и 0xFFFF). Каждый интервал представляет собой один шаг таймера IBM PC, который приблизительно равен 1/18 части секунды.

    Прикладная программа может использовать данную функцию для измерения времени (интервала) между двумя событиями. Для того, чтобы сделать это, прикладная программа выполняет два вызова данной функции, каждый из которых возвращает маркер интервала, соответствующий времени события. Затем прикладная программа вычитает первый маркер интервала из второго. Разность представляет собой временной интервал между двумя событиями (в тиках таймера). Этот результат остается точным, даже если внутренний маркер переходит через ноль между первым и вторым событием. Этот таймер не предназначен для использования с большими временными интервалами (более одного часа), вследствие точности внутреннего маркера (16 бит без знака).

    Пример ее использования смотрите в примере к функции IPXListenForSocket.

3.11. IPXCancelEvent

    Данная функция отменяет событие, определенное Блоком ECB.

     int IPXCancelEvent(ECB *eventControlBlock)

    Возвращаемые значения:

     0   (0x00)  SUCCESSFUL                 Успешное выполнение
     249 (0xF9)  ECB_CANNOT_BE_CANCELLED    ECB не может быть от-
                                            менен
     252 (0xFC)  EVENT_CANCELLED            Событие отменено
     255 (0xFF) ECB_NOT_IN_USE              ECB не используется

    Данная функция отменяет ожидающее событие посредством передачи IPX адреса ECB (Блока контроля события). Эта функция возвращает код завершения при возвращении управления прикладной программе.

    Блок ECB также может быть задействован под такие события, как отправка или ожидание сигнала - для IPX, планирование или перепланирование - для AES, или ожидание сигнала - для SPX. В этом случае его поле inUseFlag устанавливается в ненулевое значение. И попытка его повторного использования может привести к зависанию программы. Для того чтобы освободить блок для использования под новое событие, необходимо вызвать IPXCancelEvent. После этого поле inUseFlag установится в нулевое значение.

    Значения поля inUseFlag:

     Свободен
     224 (0xE0) Временный индикатор AES
     248 (0xF8) Критическая задержка
     250 (0xFA) Обработка
     251 (0xFB) Задержка (в обработке
                 после того, как произошло событие)
     252 (0xFC) Асинхронный планировщик событий ожидает     253 (0xFD) Ожидание
     254 (0xFE) Получение
     255 (0xFF) Отправление

  Пример:
             #include     
             ECB          ecb1;
             ............
             ............
             IPXListenForSocket(&ecb1);
             if (ecb1.inUseFlag)
                if (!IPXCancelEvent(&ecb1))
                    IPXSendPacket(&ecb1);
             ..............

3.12. IPXDisconnectFromTarget

    Прикладная программа может использовать эту функцию для сообщения драйверу коммуникации, что прикладная программа не собирается отправлять больше пакетов указанной станции.

    void IPXDisconnectFromTarget(BYTE *networkAddress)

    Судя по описанию "NetWare C-Interface for DOS", данная функция помогает драйверам коммуникации, которые действуют только в двухточечном "режиме" на физическом транспортном уровне. Если драйверу отправлено сообщение, он может выключать любые виртуальные соединения с указанным узлом.

    Параметр networkAddress определяет узел, с которым будет завершена коммуникация. После вызова этой функции, любые виртуальные соединения между машиной, на которой работает прикладная программа, и узлом-адресатом будут выключены драйвером.

    Из собственного опыта авторам не удалось определить реальное место применения этой функции в прикладной программе, поэтому мы затрудняемся предоставить вам пример ее использования.

3.13. IPXCloseSocket

    Данная функция закрывает сокет (гнездо IPX).

             void IPXCloseSocket(WORD socketNumber)

    Данная функция закрывает указанный в socketNumber сокет и отменяет все события, определенные блоками ECB, соответствующими ему. IPX также возвращает величину 0хFCh в поле каждого блока ECB completionCode, означающую, что событие было отменено. Наконец, IPX устанавливает inUseFlag как 00h (AVAILIABLE_FOR_USE - Доступен для использования).

    Может быть закрыт любой сокет, если он даже не был открыт - никакой ошибки не выдается. Перед тем, как прикладная программа завершается и уничтожает код ESR, она должна закрыть все открытые ею сокеты. В противном случае, подпрограмма ESR может быть вызвана после того, как она станет недоступной, что вызовет зависание на рабочей станции. Поскольку при отладке Вашей программы вам неоднократно придется прерывать ее выполнение из отладчика, чтобы избежать возможных зависаний, мы рекомендуем Вам написать простенькую утилиту закрытия сокетов, используемых в ваших программах.

    Например:

             #include     
             WORD         socketNumber=0x4001;
             main
             {
               IPXCloseSocket(socketNumber);
             }