


        
        
                           IPX: The Great Communicator
        
        
        IPX.   I  - P - X.  The tip of the tongue takes a trip  of  three 
        steps to mash against the gums with spit: I - P - X.  If IPX were 
        a  pubescent  girl,  I could wax poetic only by  robbing  from  a 
        master.  In fact, IPX is far from an object of desire, but it has 
        attracted  my attention.  In this article, I intend to  stimulate 
        that same chaste interest in the reader.
        
        I  have  no real historical perspective on IPX.  I have  come  to 
        know its present tense.  IPX is an example of the third layer  of 
        what  the  International Standards  Organization  (ISO)  proposed 
        standard called the Open System Interconnection (OSI) model as it 
        relates to Novell's Netware.  IPX allows a programmer to  perform 
        high-speed, peer-to-peer communication on Novell's Netware.   IPX 
        is  the lowest level of communication that can be performed on  a 
        network  without  resorting to directly accessing  the  hardware.  
        IPX  stands for Internetwork Packet Exchange, but is not  spelled 
        IPE.   Documentation  on IPX from Novell (in the  form  of  their 
        manual  "Netware System Calls - DOS") is poor and generally  sus-
        pect.
        
        
        The Path to IPX Enlightenment
        
        When  I  first  began using a network, the most  obvious  way  to 
        communication between nodes (eg. each computer on the network  is 
        a  node)  was to open a file and have all the nodes  access  that 
        file.   Records would be written to that open file,  flushed  and 
        then  read  by another node.  This method appeared  to  work.   A 
        couple  of  nodes on a network communicating  sparingly  in  this 
        fashion did not seem to be a problem.
        
        As  always, once a method seems to work, it gets used. And  used.  
        And abused.  I was not amused.  With a moderate load, the network 
        seemed  to  lose its pep.  Sometimes the nodes  would  not  flush 
        their  buffers properly and data would never pass into  the  open 
        file.  Many times the entire system would bog down in the message 
        pool, which would inhibit normal data access functions.  I  guess 
        sometimes  the simple solutions are a lot of the former and  none 
        of the latter.
        
        I knew that there must be a better way, so I went to the ultimate 
        source of all this aggravation - the Novellians.  This was not  a 
        good  idea.  I was able to encounter two types of  these  beasts.  
        The  first type, called an account representative (or  whatever), 
        had a pleasing smile, warm handshake and a nice suit.  The smile, 
        the  palm,  and the suit were not filled with  information.   The 
        second  type,  called a customer  support  representative  (CSR), 
        possibly  had the same attributes as the first but could only  be 
        detected  by phone.  I found that when asked a  mildly  technical 
        question,  the  CSR would report that indeed (s)he knew  all  the 






        answers  but would only speak them if I tithed an additional  10% 
        to Novell.  I was stymied.  I was titheless.  I wept, then with a 
        titanic  effort, went on with my life; cursed to live  with  this 
        file access bottleneck, forever.
        
        Forever turned out to be of a shorter duration than I had  previ-
        ously been lead to believe.  As it happened, a software distribu-
        tor  sold a set of technical manuals on Novell  Netware  program-
        ming.  The manuals ("Netware System Interface Technical Overview" 
        and  volumes 1 and 2 of "Netware System Calls - DOS")  were  just 
        what  I  needed to get started.  I recommend them to  anyone  who 
        wishes  to do any programming relating to the Netware  OS  level.  
        There  is a caveat: do not trust them.  The manuals have  several 
        deceptive  misprints that can send a programmer (namely me)  down 
        the wrong path many times.
        
        My  first  attempt  was to use Novell's  Message  Services.   The 
        message services provide broadcast and pipe services.  Fortunate-
        ly,  these services were easy to implement.   Unfortunately,  the 
        message  services  use  file server resources and  have  no  real 
        performance  edge over my original file access method.   Happily, 
        Novell  provided an apparent answer for my need for speed.  In  a 
        rare instance of developer support, the document author mentioned 
        that for "true peer-to-peer communication between programs across 
        the network, applications can use Novell's IPX or SPX protocols".  
        My  viscera  churned with excitement - IPX/SPX  was  the  answer.  
        Next  time I think that I should follow my mind rather than  some 
        undigested burgers.
        
        
        This is It, but It Ain't all there Is
        
        The  functions  that  make up the set  of  IPX/SPX  Communication 
        Services are few in number and simple in access.  The  structures 
        used by IPX/SPX are many in fields and a pain in the butt.  Let's 
        start with the bad news first, then relax with the good.
        
        Normal  Novell OS accesses are done through the  familiar  MS-DOS 
        INT-21h  window.  IPX/SPX (in order to save my fingers I'll  call 
        this  xPX from) uses a slightly different mechanism.  Before  any 
        function calls can be made, the application must call the  MS-DOS 
        multiplex and get the vector to the xPX entry point.  The  assem-
        bly code segment to get this would be as follows:
        
             mov  ax, 7a00h           ; Function 7Ah, AL = 0
             int  2fh                 ; MS-DOS Multiplex interrupt
                                      ; Returns with AL == 0FFh if xPX
                                      ;  exists and ES:DI == xPX vector
        
             inc  al                  ; Set ZERO if AL == -1
             jnz  outta_here          ; Quit if xPX isn't around
             mov  IPX_Vector, di      ; Save the xPX entry vector
             mov  IPX_Vector+2, es
        
        All  xPX  routines  are accessed by making far  calls  using  the 






        IPX_Vector.  xPX uses register BX to hold an xPX command  number.  
        An xPX call is not kind to any unused registers and, in  general, 
        sensitive registers should be saved (especially BP).  By using  a 
        far  call (rather than through MS-DOS's INT-21h), most xPX  func-
        tions  can be called from a background process without  the  pro-
        grammer worrying about trashing the system.
        
        All iPX functions return the status of their result in AL.
        
        In  my  mind, the xPX functions fall into two  major  classifica-
        tions:  initialization/information and communication.   The  ini-
        tialization/information  functions  start up the  xPX  internals, 
        open communication pathways and give the application  information 
        about  how things have been setup.  The  communication  functions 
        are  responsible  for sending/receiving  packets  of  information 
        to/from  peers  on the network.   The  initialization/information 
        functions  are simple folk with friendly structures if they  have 
        them  at  all.  The communication functions scare pit  bulls  and 
        have structures with sharp edges that make me cry.  Because I  am 
        a sadist, I will start with the communication structures.
        
        
        Terminology Reprieve:  What is an xPX Network
        
        Some  of the terminology used by Novell was foreign to me, so  in 
        order to refresh my memory, I will describe it to the reader.
          
        All network interfaces (Ethernet, Arcnet, Fishnet, etc.)  require 
        a  unique  six-byte node ID to differentiate all  network  inter-
        faces.  It should be noted that the node ID only differentiates a 
        network  interface within a single network, therefore, there  can 
        be  two  networks that have interfaces with identical  node  IDs.  
        Because a single computer can have multiple network interfaces or 
        a network interface can be placed in another computer, a node  ID 
        does not necessarily specify a single, distinct computer  system.  
        I  may be stating the obvious in the previous sentences, but  the 
        concept will be important later in this article.
        
        Every  Novell  network has a four-byte ID  number  that  uniquely 
        differentiates  it from any other Novell networks to  which  they 
        are connected.  If no other networks are connected, any number is 
        unique.
        
        Every  node  connected to a network can  open  several  different 
        channels  of  communication.  These  communication  channels  are 
        called  sockets.  Sockets allow an application (or  applications) 
        to differentiate types of communication performed by a node.  One 
        socket  can be used to broadcast that "Mars needs women!" to  all 
        nodes  while  another socket can be used to send  and/or  receive 
        messages from specific nodes or groups of nodes.  By default, xPX 
        can support up to 20 sockets on a single node.  Through  configu-
        ration,  the number of sockets that may be open can be  increased 
        to 150.  A two-byte number is used as a socket ID.
        
        Numerically, the network, node and socket IDs are in small-endian 






        (Motorola) format.  The format of the IDs is really of no  conse-
        quence because the magnitude, order and content are not  relevant 
        to the application or xPX.  There are special cases of each  type 
        of ID, but all are palindromes; so byte order is still not impor-
        tant.   The four-byte network ID can be viewed as a  32-bit  long 
        integer  without any significance associated with its  magnitude.  
        The six-byte node ID can be viewed as a non-ASCII string, such as 
        a  filename, with no naming convention.  The two-byte  socket  ID 
        can be a short integer, also with no significance associated with 
        its magnitude.
        
        
        xPX Structures
        
        The  basic  control structure used with both IPX and SPX  is  the 
        Event Control Block (ECB).  The ECB is passed to xPX to  describe 
        its  associated channel and buffers.  The first 34 bytes  of  the 
        ECB  contains  control  and addressing information.   A  list  of 
        associated  fragments  (Novell's  term for chunks  of  byte  real 
        estate) immediately follow this 34 byte header.  The following is 
        the format for an ECB:
        
             Region    Description
             ------    -----------
              0 - 3    Link to next ECB, filled and used by xPX, while 
                       this ECB is not used by xPX, the application can
                       use this field for its own ECB management.
        
              4 - 7    Far pointer to the Event Service Routine (ESR)
                       that is associated with this ECB.  This can be
                       NULL, if asynchronous processing is not desired.
        
              8 - 8    In Use Flag.  Used by xPX to show the current
                       state of the ECB processing.  Set to 0 when xPX
                       is done with the ECB.
        
              9 - 9    Completion Code.  Set by xPX when the ECB In Use
                       Flag is set to 0.  This field is only valid when
                       xPX has finished with the ECB.  A 0 indicates that
                       the ECB task was completed successfully.  Any
                       other value indicates an error condition.
        
             10 -11    Socket ID.  Set by the application to tell xPX
                       with which socket that the ECB is to communicate.
        
             12 -27    Used internally by xPX.
        
             28 -33    Immediate Address.  This is the local node ID with
                       which this ECB is to communicate.  If the ECB
                       is being sent (a talker) using IPX (rather than
                       SPX), this field should be filled in by the appli-
                       cation.  If the ECB is a listener or uses SPX,
                       this field is filled by xPX.






        
             34 -35    Fragment Count.  This word is filled in by the
                       application to tell xPX how many fragment descrip-
                       tors are to follow.  All ECBs must have at least
                       one fragment descriptor to point to an xPX packet
                       header.  The cumulative size of the fragments
                       that are associated with an ECB can not exceed 576
                       bytes.
        
             36 -39    Fragment Pointer 1.  Far pointer to the first
                       fragment associated with the ECB.  The application
                       must have at least one fragment that contains a
                       complete IPX or SPX header.  Any additional data
                       buffers can be contiguous extensions of the xPX
                       header or segmented into unconnected memory loca-
                       tions.  Non-contiguous memory fragments would re-
                       quire more than one fragment descriptor.
        
             40 -41    Fragment Size 1.  Number of bytes that are in the
                       first fragment.  If the size of the first fragment
                       is not at least the size of either an IPX or SPX
                       header, xPX returns an error.  Fragments that fol-
                       low must contain at least one byte.
        
             42 -45    Fragment Pointer 2.  This fragment descriptor and 
                       ones that follow is optional and need not be used
                       or declared if the first fragment descriptor de-
                       scribes the packet buffer in its entirety.
        
             46 -47    Fragment Size 2.  Optional size of the second 
                       fragment.
        
               ...          ...
        
        
             nn -nn    Fragment Pointer n.
        
             mm -mm    Fragment Size n.
        
        
        The  Event  Service Routine (ESR) is a function provided  by  the 
        application  that  is called by xPX when the ECB has  been  proc-
        essed.  The ESR is called by xPX in the following  condition:
        
             ES:SI points to the ECB that was processed,
        
             all registers except SS and SP have been saved on the stack,
        
             interrupts are disabled,
        
             AL is 0FFh if called by IPX, or 0 if called by the Asynchro-
             nous Event Scheduler,
        
             all segment registers are in unknown states.
        






        An ESR must conform to the following conditions:
        
             return with a RETF instruction and interrupts disabled,
        
             maintain the stack's integrity,
        
             can call any xPX function except Close Socket,
        
             can reschedule itself through an AES call,
        
             can  enable interrupts during operation as long as  blocking 
             is done against another event calling the same function.
        
        Conceivably,  an application could have a separate ESR for  every 
        ECB;  however, that approach would be excessive.  The ESR  should 
        be made fairly general propose and should be approached with  the 
        same mindset as one would an Interrupt Service Routine.
        
        There  are  a variety of states that the In Use  Flag  field  can 
        reflect  as the ECB transitions toward completion.   These  state 
        values  are defined within my header file named  NETWORK.H.   All 
        definitions  associated  with the In Use Flag are  prefixed  with 
        IU_.   The  application can poll In Use to check on  the  current 
        status  of  a particular ECB or wait until the ESR is  called  by 
        xPX.
        
        The Completion Code field is filled by xPX when the ECB has  been 
        processed.   As the contents of this field have no meaning  until 
        the  In  Use Flag equals 0, the application should not  have  any 
        expectations  of  Completion Code until that time.  Many  of  the 
        Novell  documented completion codes are defined within  the  file 
        named NETWORK.H.  All definitions associated with Completion Code 
        are prefixed with CC_.
        
        The  first  fragment  descriptor must point to  either  a  header 
        structure  for an IPX or SPX packet before the ECB can be  passed 
        to  any  xPX  function.  Both packet types start  with  the  same 
        structure.  The format of the IPX header is as follows:
        
             Region    Description
             ------    -----------
              0 - 1    Checksum.  Set by xPX to -1.
        
              2 - 3    Length of the entire xPX packet, including all
                       other fragments associated with the ECB.  Filled
                       by xPX.
        
              4 - 4    Transport Control.  Set to 0 by xPX.
        
              5 - 5    Packet Type. Set by the application to 4 for an
                       IPX packet or 5 for an SPX packet if the packet is
                       being sent (talker).






              6 - 9    Destination Network ID.  Set by the application if 
                       the xPX packet is being sent (talker).  If this is
                       set to 0, the current network is used regardless
                       of its true ID.
        
             10 -15    Destination Node ID. Set by the application if the
                       packet is being sent (talker).  If this field is
                       set to all 0FFh's, the IPX packet will be sent to
                       all IPX listeners on the network with an equal
                       socket ID, including active listeners on the
                       transmitting node.
        
             16 -17    Destination Socket ID.  Set by the application if 
                       the packet is being sent (talker).  The socket ID 
                       must have been opened before the socket ID can be
                       used.
        
             18 -21    Source Network ID.  Set by xPX to the network ID
                       of the packet's source.
        
             22 -27    Source Node ID.  Set by xPX to the node ID of the
                       packet's source.
        
             28 -29    Source Socket ID.  Set by xPX to the socket ID of
                       the packet's source.
        
        The  application  needs  to fill the IPX structure  only  if  the 
        packet  is being transmitted.  If the packet is being used  as  a 
        listener, xPX fills in all the fields.
        
        The  SPX  packet header is a superset of the IPX  packet  header.  
        The SPX structure is as follows:
        
             Region    Description
             ------    -----------
              0 -29    IPX header previously defined.
        
             30 -30    Connection Control.  Used by SPX to control the
                       flow of data.
        
             31 -31    Datastream Type.  Information byte that can be
                       used by the application for any purpose.  SPX re-
                       serves values 0FEh and 0FFh for its own use.
        
             32 -33    Source Connection ID.  Connection number of the
                       source node for this SPX packet.  This is created
                       by SPX for use by the application.
        
             34 -35    Destination Connection ID.  Connection number of 
                       the destination node for this SPX packet.  This is
                       created by SPX for use by the application.
        
             36 -37    Sequence Number.  Used by SPX to keep the sequence
                       of received and transmitted packets straight.
        






             38 -39    Acknowledge Number.  Used by SPX to acknowledge
                       receipt of a packet.
        
             40 -41    Allocation Number.  Used by SPX to keep track of
                       packets that have been sent but not acknowledged.
        
        None  of  the  SPX specific fields need to be filled  in  by  the 
        application for either transmissions or receptions.  These fields 
        must be available for SPX packets.
        
        The  size  of an SPX packet header is 12 bytes  larger  than  the 
        header  for  IPX packets.  Because the headers must  be  part  of 
        every xPX packet, the maximum size of data that can be sent  with 
        an IPX packet is 546 bytes and the maximum for SPX is 534 bytes.
        
        
        IPX Functions
        
        An  introduction  to the IPX functions can be approached  in  the 
        same  order  that an application would have need of  those  func-
        tions.  A full explanation of each of the IPX functions would  be 
        beyond  the scope of this article.  I will give each  function  a 
        brief  description  and leave it to the reader to look  over  the 
        code samples provided for a more precise mechanism.
        
        Once  the  application has the xPX access vector,  some  internal 
        initialization  needs  to be performed.  First,  the  application 
        might want to know its own network and node ID.  These IDs can be 
        discovered by performing an IPX Get Internetwork Address call  to 
        xPX.  This is performed by the following code segment:
        
             mov  bx, 9          ; BX = IPX Get Internetwork Address cmd
        
             mov  ax, ds         ; ES:SI -> structure to fill with the
             mov  es, ax         ;          network and node IDs
             mov  si, offset reply_buffer
        
             call dword ptr IPX_Vector
        
        This  function  will always return good status (0)  in  AX.   The 
        buffer  pointed  to by ES:SI will be filled  with  the  following 
        information:
        
             Region    Description
             ------    -----------
              0 - 3    Network ID of the application's computer
        
              4 - 9    Node ID of the application's computer
        






        
        The application would need to open a socket ID in order to commu-
        nicate  with  the other peers.  The following code  sample  would 
        open a socket ID:
        
             mov  bx, 0          ; BX = IPX Open Socket command
             mov  dx, socket     ; DX = socket ID to open
        
             mov  al, life_time  ; AL = socket longevity flag
        
             call dword ptr IPX_Vector
        
        This function returns the socket number that was opened in DX and 
        a completion status in AL.  Valid returns from this function are:
        
             00h       Socket opened
        
             0FFh      Socket previously opened, no problem
        
             0FEh      Socket table is full, bummer
        
        The socket longevity flag informs xPX of how long the application 
        intends  to keep the socket open.  A 0 indicates that the  socket 
        will be closed explicitly by the application or xPX should  close 
        it  when the application exits back to MS-DOS.  A 0FFh tells  xPX 
        to  close  the socket only when explicitly told to do so  by  the 
        application.  A 0FFh life allows the application to TSR and still 
        maintain an open socket ID.
        
        Socket IDs can not always be arbitrarily used.  Novell and  Xerox 
        have reserved the range of socket IDs from 1 to 0BB8h, inclusive.  
        An  application may use the socket IDs within the range of  4000h 
        to 7FFFh.  Novell reserves the range of IDs from 8000h to  0FFFFh 
        as  they see fit.  If the application requests socket ID 0,  Net-
        ware will dynamically allocate a socket ID in the range of  4000h 
        to 7FFFh.  Some of the documented socket IDs used by Netware  are 
        as follows:
        
             451h      Used by Netware file servers to for service
                       requests.
        
             452h      Service Application Packet (SAP).  Used by appli-
                       cations (or servers) to broadcast their services
                       to all nodes that care.
        
             453h      Routing Information Packet.
        
             455h      Used by NetBIOS.
        
             456h      Diagnostic packet.
        
        
        When  the application has opened a socket ID, it must setup  lis-
        teners  and/or  talkers.  To create a listener,  the  application 
        must  first create an ECB and fill in the fields that  deal  with 






        listening.   Next, the application needs an IPX header  structure 
        and  a  buffer in which the received data will  be  placed.   The 
        fragment descriptor(s) of the ECB should point to the IPX  header 
        and  the  destination data buffer.  If the IPX  header  and  data 
        buffer  are  one contiguous memory block, only one  fragment  de-
        scriptor  is required.  When the ECB, IPX header and data  buffer 
        are setup, a far pointer to the ECB is sent to xPX.  The ECB sits 
        with  xPX until a packet comes in from the network  that  matches 
        the ECB's socket ID.  When the received packet is transferred  to 
        the  listeners  structures, the In Use Flag is set to 0  and  the 
        Completion  Code field is set appropriately.  If an ESR was  pro-
        vided by the ECB, the ESR is called by xPX.  The ECB is then free 
        to be used by the application.
        
        To  send  an ECB off to the listening post,  the  following  code 
        fragment could be used:
        
             mov  bx, 4                    ; BX = IPX Listen For Packet
             les  si, offset ECB_Packet    ; ES:SI -> ECB for the packet
        
             call dword ptr IPX_Vector
        
        The function returns immediately with the status in AL.  A return 
        of 0 indicates that the ECB has been placed in a listening queue, 
        waiting for the next incoming packet.  A return of 0FFh indicates 
        that the socket ID set within the ECB has not been opened.
        
        The  application  should either poll the In Use  Flag  field  for 
        completion or wait for the ESR to be called by xPX.
        
        The application can send any number of listening packets to  xPX.  
        If  a packet is received by xPX and no listener is ready  to  re-
        ceive it, the packet and its data are lost.  If a packet is lost, 
        IPX does not inform either the destination or the source node  of 
        this condition.  IPX does not guarantee delivery, SPX does.
        
        The requirements for sending a packet is similar to setting up  a 
        listening packet.  The ECB, IPX header and data buffer are set up 
        with  pertinent  information.  xPX can then be  called  with  the 
        following code fragment:
        
             mov  bx, 3                    ; BX = IPX Send Packet command
             les  si, offset ECB_Packet    ; ES:SI -> ECB for the packet
        
             call dword ptr IPX_Vector
        
        The  function returns immediately, but has no status in  AL.   As 
        with   listening,  the  application  should  poll  the   In   Use 
        Flag/Completion Code fields for any result of the transmission.
        
        If  the transmitted packet does not reach its target, the  source 
        node is not informed.
        
        The  Immediate  Address field of the ECB needs to be  filled  for 
        talking  packets.  If the target node is on the same  network  as 






        the   source   node,  the  Immediate  Address   is   merely   the 
        destination's  node ID.  If multiple networks are connected,  the 
        application  needs to query xPX as to what the Immediate  Address 
        is  for a full node and network ID.  That query is made with  the 
        following code fragment:
        
             mov  bx, 2          ; BX = IPX Get Local Target command
             mov  ax, ds         ; ES = data segment of the request and
             mov  es, ax         ;      reply buffer
        
             mov  si, offset request_buffer
             mov  di, offset reply_buffer
        
             call dword ptr IPX_Vector
        
        On entry ES:SI should point to the following structure:
        
             Region    Description
             ------    -----------
              0 - 3    Network ID of the destination node
        
              4 - 9    Node ID of the destination node
        
             10 -11    Socket ID of the destination node
        
        This  function  returns  the status of the operation  in  AL.   A 
        status  of 0 indicates that the network and node were  found.   A 
        status of 0FAh indicates that xPX could not found a network  path 
        to  the node.  If the local target was found, CX will contain  an 
        estimate of the number of system ticks (1/18th of a second)  that 
        a  packet would take to arrive at the destination node's  socket.  
        If  the node was found, the six byte buffer pointed to  by  ES:DI 
        will be filled with the Immediate Address of the target node.  If 
        the node is on another network, the local address is the node  ID 
        of the bridge to the other network.
        
        When  the application has completed its transmissions and  recep-
        tions,  it should disconnect from its target node(s), cancel  any 
        active events and close any open socket IDs.  Informing peers and 
        closing  socket  IDs are not as important as canceling  any  out-
        standing  events.   If  an ECB is still in xPX's  queue  and  the 
        associated  ESR vector is not NULL, xPX will still call that  ESR 
        without regard to whether the program is still operating.  A call 
        to an ESR of a program that has terminated will probably hang the 
        computer system.  If the peer is not informed of closure, packets 
        will be ignored, but no catastrophic failure will occur.  If  the 
        socket ID's longevity flag was 0 (eg. a mere mortal), the  socket 
        will be closed automatically when the program terminates.






        The following code fragment will cancel an IPX event:
        
             mov  bx, 6          ; BX = IPX Cancel Event command
             les  si, ECB        ; ES:SI -> ECB to cancel
        
             call dword ptr IPX_Vector
        
        Status  is  returned  in AL.  If AL is 0, the  ECB  was  canceled 
        successfully  and an 0FCh is placed in the Completion Code  field 
        of  the ECB.  If AL is 0FFh, the ECB was not scheduled  for  use.  
        If  AL is 0F9h, the ECB cannot be canceled - possibly because  it 
        is  at  the moment of transfer.  In most  instances,  the  return 
        status from a Cancel Event command is immaterial
        
        A canceled ECB does not call its associated ESR.
        
        The  following  code fragment can be used to  disconnect  from  a 
        target address:
        
             mov  bx, 0Bh        ; BX = IPX Disconnect From Target cmd
             mov  ax, ds         ; ES:SI -> full address of target
             mov  es, ax
             mov  si, offset address
        
             call dword ptr IPX_Vector
        
        On entry ES:SI should point to the following structure:
        
             Region    Description
             ------    -----------
              0 - 3    Network ID of the target
        
              4 - 9    Node ID of the target
        
             10 -11    Socket ID of the target
        
        No status is returned from this function.
        
        Finally, in order to explicitly close a socket ID, the  following 
        code fragment can be used:
        
             mov  bx, 1          ; BX = IPX Close Socket command
             mov  dx, socket_ID  ; DX = socket ID to close
        
             call dword ptr IPX_Vector
        
        There is no status returned by this function.  If the socket  was 
        never  open,  IPX  does not care.  If the socket  was  open,  all 
        events  that  were associated with the socket by  previous  calls 
        will be canceled.  The closure of a socket ID and its  associated 
        events does not trigger a call any ESRs.
        
        We  are now done with the nickels and dimes, let's go on  to  the 
        folding money.






        
        Simplicity Breeds Attempts
        
        After  I figured out the structures and IPX calls, I brewed  some 
        tea, started up my "Police" CD, and began to code.  I wrote  some 
        IPX  access  functions, filled the structures  and  sent  packets 
        between  two  nodes.  It worked well.  Packets  were  coming  and 
        going nicely, politely and without messing my carpet.  I  figured 
        that I was ready for the big time; so, I added my new programming 
        gems to an application.
        
        At first, IPX demonstrated itself to be superior the file or pipe 
        methods.  At last, IPX began to lose packets.  I found that under 
        a moderate transmission load, my application would spend too much 
        time  minding its business while IPX's filled  listening  packets 
        would not be serviced.  Polling was not able to keep up with  the 
        data transfer demand.
        
        I had been deluded by Sting, "Asynchronicity" was the answer.  If 
        more  than a few packets a second are to be transferred,  an  IPX 
        communication needs ESR support in order to function effectively.  
        It  should  be noted that even with ESR support  packets  can  be 
        lost.
        
        According  to  Novell's  documents, IPX will yield  about  a  95% 
        delivery rate.  My experimentation with ESR powered IPX yielded a 
        100%  delivery  rate for over 100,000 packet  transmissions.   To 
        find  a  reasonable upper limit, I ran a program on  four  80386-
        based computers on my Ethernet based network.  Each computer sent 
        and received 512-byte IPX packets continuously.  I did not notice 
        any  packet loss until they reached about 70 packets  per  second 
        per  node (280 packets per second, overall).  At that point,  the 
        16-MHz 386SX started to go deaf.
        
        After I implemented ESR support, I decided that the functions and 
        data  structures  were cumbersome.  At an  application  level,  I 
        didn't  want  to  have to deal with  the  asynchronous  approach.  
        Because I was already using a file based approach, I figured that 
        the entire IPX access would be best implemented as a streams type 
        of  interface.  With a streams implementation,  analogous  struc-
        tures  became  apparent.   The  full  network  address  structure 
        (network  ID,  node  ID, and socket ID)  became  the  "filename".  
        Access flags could be used to define the type of packets (listen-
        ers or talkers) and communication method (IPX or SPX).  I  merged 
        the ECB and a superset structure of the IPX/SPX header.
        
        To keep track of the operation of the stream, I created a  struc-
        ture  called  XPX_STREAM_T  which  is  documented  in  the   file 
        NETWORK.H.  XPX_STREAM_T allows the programmer to open a  channel 
        to  a  node (or nodes if it is a broadcast channel)  and  perform 
        reads, writes and queries on that channel.  Multiple xPX  packets 
        are automatically allocated and provided for the stream's I/O.






        Coding Conventions
        
        The  reader  is invited to peruse the source code  provided  with 
        this  article.  A few explanations about the  coding  conventions 
        that  I use might be helpful.
        
        All whole words within variable names, function names and #define 
        labels  are  separated by underscores (_).  Merged words  have  a 
        habit  of  mutating into a third, possibly  meaningless  amalgam.  
        Addit, abbrs r kpt 2 a min.
        
        In  C, there is no real delineation between functions and  varia-
        bles.   Functions  are  merely arrays of code.  I  use  names  to 
        differentiate  these two types.  Functions denote action,  there-
        fore,  all functions start with verbs.  Exceptions are made  only 
        if a group of functions form a complete unit.  These group excep-
        tions  are given the same first word (which may be a  noun)  fol-
        lowed by a verb.
        
        Data  variable  name are the object of the verb, so  they  always 
        start with a noun.  Local variable and passed parameter names are 
        completely  in  lower case.  Global and  static  global  variable 
        names  start  each whole word with a capital letter  followed  by 
        lower  case.  This allows the programmer to immediately  evaluate 
        the scope of a variable that (s)he is about to change.
        
        Definition  labels are completely in upper case to  differentiate 
        them from variables that occupy memory.  As with data  variables, 
        the first whole word of these labels is a noun.
        
        Source code symmetry is important.  Starting braces always appear 
        directly below the first letter of the token with which they  are 
        associated.  Finishing braces always appear in the same column as 
        their  matching  starting  brace.  Because english  is  my  first 
        language,  I  read from left to right, therefore, I never  put  a 
        starting  brace on the extreme right of the display.  A  function 
        is analogous to a chapter in a book.  I always look the left side 
        of  the page when I start a chapter, so I would expect to do  the 
        same with a function.
        
        
        Descriptions of Major Functions
        
        The   major  functions  written  for  the  stream  approach   are 
        XPX_INIT(), XPX_OPEN(), XPX_READ(), XPX_WRITE(), XPX_CLOSE()  and 
        IPX_READ_ORPHAN().  Except for XPX_INIT() and  IPX_READ_ORPHAN(), 
        the  functions  are used in an analogous fashion  to  their  file 
        counterparts.   The  following paragraphs will describe  each  of 
        these functions.
        
        XPX_INIT()  initializes  the  SPX internals and  gets  the  entry 
        vector  for  accessing IPX.  This function queries  IPX  for  the 
        application's node ID and network address which is placed in  the 
        global structure Our_Address.  After xPX is initialized, a single 
        socket  ID  is  opened and dynamically generated  by  IPX.   This 






        socket ID is placed in the Our_Address structure.  It can be used 
        as  an  application's private channel.  This open socket  is  not 
        needed  for the operation of any functions, and is only  provided 
        for the sake of convenience.
        
        XPX_OPEN() allows the application to open a communication channel 
        to  another node on the network.  The channel can be  read  only, 
        write  only  or  read/write.  The node ID can be  specific  or  a 
        broadcast channel.  A specific open will only receive packet from 
        the  defined  target node ID.  A broadcast  channel  will  accept 
        packets from any node ID at that socket.  There is no such  thing 
        as  a broadcast socket, so socket IDs are important.  Any  packet 
        received  by  a node from itself will be ignored.  It  should  be 
        noted  that an IPX stream cannot share the same socket as an  SPX 
        stream.  Multiple IPX streams can share the same socket.
        
        XPX_READ()  allows  the  application to read  the  next  received 
        packet data partially or completely.  The read will only be  made 
        up to the end of the first available packet.  Data from  multiple 
        packets  can only be read by successive reads.  Packets may  only 
        be read in the order in which they were received from the  source 
        node.
        
        XPX_WRITE()  allows  the application to write data to  a  stream.  
        The  data  written  does not have to be  of  a  particular  size.  
        XPX_WRITE() will packetize the data and send the packets to  xPX.  
        XPX_WRITE() returns the number of bytes that were written.  If  a 
        done  pointer is provided for IPX_WRITE(), IPX_WRITE()  will  set 
        the flag to a non-zero value and the ESR will reset the flag to 0 
        when a packet has been sent.
        
        XPX_CLOSE()  closes the communication channel and frees all  data 
        buffers allocated by XPX_OPEN().
        
        IPX_READ_ORPHAN() allows the application to read any packets that 
        have  been sent to a socket ID but do not match any  open  stream 
        addresses (broadcast streams with read ability match all  nodes).  
        This function should be called periodically, so all the listening 
        packets  do  not get used up by spurious receptions.   If  orphan 
        packets  are  not an issue, the application can  set  the  global 
        variable _Ignore_Nomatch to a non-zero value.
        
        Four macros have been defined within NETWORK.H to provide  status 
        information for the application.  XPX_ERROR_STATUS() returns then 
        number of packet errors that have occurred since the last call to 
        IPX_INIT().   XPX_ORPHAN_STATUS() returns the current  number  of 
        unprocessed orphan packets.  XPX_READ_STATUS() returns the number 
        of  packets  associated with a stream that have  not  been  read.  
        XPX_WRITE_STATUS() returns the number of packets that are  avail-
        able to a stream for transmission.
        
        The  file TEST1.C has been provided to give the reader a  working 
        program  that  uses the major xPX functions.  TEST1.C is  run  on 
        multiple  network nodes and continually transfer packets  to  any 
        other nodes from which it receives broadcasts.






        
        Conclusion
        
        IPX  is a high performance protocol available on  Novell  Netware 
        networks.   IPX  adds a level of complexity  and  uncertainty  to 
        peer-to-peer  communications, but can be fashioned into a  useful 
        resource.  SPX is slightly slower, but guarantees data  delivery.  
        Given the scope of this article, I have only touched upon SPX and 
        dwelled  upon  IPX.  I leave these functions as a  foundation  on 
        which the reader can build.  I suppose that I could write further 
        about  SPX and the other protocols, but my tea is  finished,  the 
        "Police"  have  been ejected for bearing false witness,  and  I'm 
        tired.  Goodnight.
