/*------------------------------------------------------------------------*/
/*                                                                        *
 *  $Id: Valuable_Concepts 1.1 1994/11/29 13:36:31 heinz Exp $
 *                                                                        */
/*------------------------------------------------------------------------*/

nipc.library
============

NIPC is a new library designed to provide a high level interface to
networking hardware.  By using NIPC, programmers will not have to deal
with all of the issues involved in networked communications such as
sequencing, reliability, etc. NIPC has also been designed to be both
easy to use and yet very powerful.

The NIPC API was designed in such a way that it would seem familiar to
Amiga programmers that are familiar with Exec Messages and Device I/O.
Many NIPC function calls such as GetTransaction(), DoTransaction() and
ReplyTransaction() strongly resemble function calls already present in
the Amiga OS.

At the lowest level, NIPC uses standard SANA-II device drivers so that
it is capable of running on virtually any networking hardware,
including ARCNET, Ethernet and even serial ports via SLIP.

NIPC transfers data between machines with primarily two concepts -
"Entities" and "Transactions".


Entities and Transactions
=========================

An Entity is nothing more than a communications "endpoint". Any data
sent using NIPC is sent between two of these endpoints - between two
Entities.  An Entity is known by two characteristics: it's name (an
ASCII string of up to 63 characters plus a terminating NULL), and the
name of the machine on which it resides.   These Entities are
guaranteed uniqueness by the fact that NIPC does not allow two
Entities to exist on the same machine with the same name.

A given machine can have an unlimited number of Entities, each
communicating with an unlimited number of other Entities. NIPC allows
communication between Entities by establishing communications "paths"
between individual Entities.  These paths are considered
unidirectional in the sense that only the Entity that created the
communications path can initiate a data transfer.  However, the
"paths" are also bidirectional in the sense that data can flow in both
directions.  In the diagram below, four Entities named 'A', 'B', 'C'
and 'D' exist with various lines connecting the Entities together.
Each of the lines indicates one of these data paths.  In the case
below, Entity A has a path to Entity C and also has a path to B.  B
has a path to D and also a path back to A.


         -                         -
        |A|   ---------------->   |B|
         -    <----------------    -
         |                         |
         |                         |
         |                         |
         |                         |
         V                         V
         -                         -
        |C|                       |D|
         -                         -

A data path is created by one Entity attempting to locate, or 'find'
another Entity.  This is done with the library function FindEntity().
Conversely, a data path is removed by the library function
LoseEntity().  These data paths exist only as long as the creator of
the path wishes it to exist (or until one side of the path disappears
for some reason).

Data is transferred from one point to another in Transactions. A
Transaction is defined by a structure called the Transaction
structure.  These are allocated and freed by the library functions
AllocTransaction() and FreeTransaction().

A Transaction is always initiated by a program that has previously
created a data path using the FindEntity() function. The transaction
is then sent to a destination Entity, which deals with it however it
wishes. The transaction is then returned to the sender.  As said
before, these data paths are somewhat unidirectional.  In the above
diagram, Entity A is allowed to send a Transaction to Entity C - who
will receive it and send it back.  However, since Entity C has no data
path to Entity A - it cannot initiate a Transaction.  However, both
Entity A and Entity B established seperate data paths to each other
allowing either to initiate a Transaction to the other.

A Transaction does allow for bidirectional data flow, however. The
sender of a Transaction can attach "Request data" to it that the
receiver can read.  The receiver is allowed to place data into a
"Response buffer" and return that data back to the sender.

A Transaction can at any time be in one of three phases: the Request
phase, the Servicing phase, and the Response phase.

The following diagrams may be useful in understanding each of these
phases.  In the diagram below, one machine is about to communicate
with another machine.  A program on machine 1 has succeeded at a
FindEntity(), thereby locating and connecting to Entity B on Machine
2.  Now that the program has found Entity B, it attempts to begin a
Transaction by using either BeginTransaction() or DoTransaction().
Once the program has Begin/DoTransaction()'d, NIPC transmits the
Request data portion of the Transaction to Machine 2.  This phase of
the Transaction is the Request phase, and is shown in Diagram 1.

When the Transaction is received by NIPC on Machine 2, it is placed
onto the destination Entity - Entity B in this example. While it
remains on this Entity, the Transaction remains in the Request phase.
Once the server program on Machine 2 attempts a GetTransaction() on
that Entity and removes the given Transaction from the Entity, the
Transaction enters the Servicing phase. This is shown in Diagram 2.

While the server program attempts to fufill the needs of the
Transaction the Transaction remains in the Servicing phase.  When
the server program has finished with the Transaction and attempts
to return it to Machine 1 by calling ReplyTransaction(), the
Transaction enters the Response phase.  The response data is
transmitted back to the original sender (Machine 1) and the
Transaction is returned to the source Entity (Entity A) as shown
in Diagram 3.  The Transaction remains in this phase forever - or
until it is reused.  The trans_Type field indicates which phase a
given Transaction is in.


     MACHINE 1                            MACHINE 2
      ______                               ______
     |Entity|        Request Data         |Entity|
     |  A   |        ------------->       |  B   |
     |______|                             |______|
                    Request Phase
                      Diagram 1.


     MACHINE 1                            MACHINE 2
      ______                               ______
     |Entity|                             |Entity| (Server has
     |  A   |                             |  B   |  Transaction)
     |______|                             |______|
                    Servicing Phase
                      Diagram 2.


     MACHINE 1                            MACHINE 2
      ______                               ______
     |Entity|       Response Data         |Entity|
     |  A   |        <------------        |  B   |
     |______|                             |______|
                    Response Phase
                      Diagram 3.





Analogies and Similarities
==========================

NIPC shares many concepts with Exec messages and device I/O.  One such
concept is the idea that applications communicate with other
applications or devices by sending and receiving finite pieces of
data, usually in the form of an Exec Message. NIPC also shares the
concept of a communications endpoint similar to the Exec Message Port.

In NIPC, the communications endpoint is known as an Entity. Entities
are used as both the source and destinations of data being sent
between two applications.  The data being sent between applications is
known in NIPC as a Transaction.

Shown below are two diagrams that illustrate how Messages and Ports
relate to Transactions and Entities.


Application<-->MsgPort<----Message----->MsgPort<-->Application

Application<-->Entity<---Transactions--->Entity<-->Application


Unfortunately, it gets more complicated than the above diagram can
show.  Unlike Exec Messages, NIPC Transactions are not guaranteed to
arrive at a destination Entity in a preset amount of time.  In fact,
it is possible that a Transaction will never make it to it's
destination due to machine and/or network failures.  NIPC does make a
good effort at delivering transactions, however.  The lower level
protocols used in NIPC help to provide this functionality.

Another difference between NIPC communications and Exec Messages is
that NIPC is designed around a client-server model. In many cases the
two applications communicating will be a client and a server, such as
the case with a print spooler or a network filesystem.  Because of
this, clients have more control over the transactions than the servers
do.  This will become apparent later on.


Using NIPC
==========

Before communication via NIPC can begin, each application that will be
involved must create an Entity.  This is done by calling
CreateEntity().  Once an application has created it's own entity, it
must find the other application's entity.  Typically, the entity to be
found is a public Entity created by a server.

To find an Entity created by another program, you call FindEntity().
FindEntity() has parameters that allow you to specify the name of the
Entity, the name of the host that you are trying to find the Entity
on, and the Entity that you will be using for the source of your
Transactions.

The reason that FindEntity() also requires a pointer to your program's
Entity is that NIPC communication is connection oriented.  This means
that a connection forms between your Entity and the Entity you found.
This connection will continue to exist until you no longer need it or
until a permanent network failure or machine failure occurs.  When you
are done communicating with the other Entity, you call LoseEntity().
The concept of a connection does not exist with Exec Messages.  This
is why you must use a Forbid()/Permit() pair when communicating with
public message ports to guarantee that the port does not go away in
between the time you call FindPort() and PutMsg().

Once you have created your entity and found the remote entity, you
must allocate a Transaction structure.  The only supported way of
doing this is to call the NIPC function AllocTransaction().

AllocTransaction() allows you to optionally specify buffer sizes for
any data being sent and/or any data that may be received that the
Transaction is complete.  You may also allocate your own buffers, but
you are responsible for freeing them yourself.

A Transaction is sent on it's way by calling either BeginTransaction()
or DoTransaction().  BeginTransaction() will return immediately unless
you are trying to send more data than the network protocols are
capable of handling at the moment. Once your Transaction has begun,
BeginTransaction() will return. It should be noted that the amount of
time that BeginTransaction() will block will usually be quite small.
Therefore, BeginTransaction() should be considered an asynchronous
call.

DoTransaction() will block until either the Transaction has completed
or an error occured.  Because your program will not be able to
anything until DoTransaction() returns, it is strongly suggested that
you try to avoid it, or spawn a separate task or process to call
DoTransaction().

When the Transaction returns, you should examine it to determine how
much, if any, data was returned and whether or not an error condition
occured.

Programs acting as servers usually do not call AllocTransaction(),
BeginTransaction() or DoTransaction(). Typically, a server will wait
for a Transaction to arrive at it's public Enitity.  This may be done
by using either WaitEntity() or by using the Exec Wait() function.  To
see if a Transaction is available at your Entity, you should call
GetTransaction() which will eithe return a pointer to a Transaction or
NULL if there are not more transactions to be processd.

For each transaction you receive, you should examine it to determine
what you should do with the data in it, or if you need to return some
data to a client.  Once you have finished processing the transaction,
you must return it to it's sender by calling ReplyTransaction().

For an example NIPC client and server, please see the two files named
RemoteRequest.c and RequestServer.c.  These two programs allow you to
put up a requester on a remote machine running NIPC.


Structure and Function call Overview
====================================

While the text below will give you a useful introducation into what is
needed to use nipc.library, you should in any case refer to the
autodocs and include files for nipc.library to get the complete
picture. Only the most important things are outlined here and some
stuff is omitted.

struct Transaction
{
        struct Message trans_Msg;
        struct Entity *trans_SourceEntity;
        struct Entity *trans_DestinationEntity;
        UBYTE          trans_Command;
        UBYTE          trans_Type;
        ULONG          trans_Error;
        ULONG          trans_Flags;
        ULONG          trans_Sequence;
        APTR           trans_RequestData;
        ULONG          trans_ReqDataLength;
        ULONG          trans_ReqDataActual;
        APTR           trans_ResponseData;
        ULONG          trans_RespDataLength;
        ULONG          trans_RespDataActual;
        UWORD          trans_Timeout;
};


    trans_Msg

        Used internally by nipc.library.  Do not use or examine.

    trans_SourceEntity

        This is the entity from which the Transaction originated.
        This is filled in by Begin/DoTransaction().  It may be
        examined by the responder.  The common use of this field is
        for a responder who has just received a Transaction from
        another Entity to pass into GetHostName() or GetEntityName() -
        to determine the name of the Entity that sent the Transaction,
        and the name of the machine from which that Transaction
        originiated. Note that this must be done before replying the
        Transaction. The responder must not cache the value.

    trans_DestinationEntity

        Used internally by nipc.library.  Do not use or examine.

    trans_Command

        This field is filled in by the originator of the Transaction
        and may be examined by the responder.  It is generally only
        read by the responder.  This field is generally used to
        indicate the particular action the originator desires the
        responder to perform.  It is analogous to the IORequest
        io_Command field. Note that, unlike with Exec Device I/O,
        there are no standard responder commands which all requesters
        can expect all responders to understand.

    trans_Type

        One of TYPE_REQUEST, TYPE_RESPONSE or TYPE_SERVICING.  A
        Transaction is always in one of three phases - the Request
        phase, between the time it is Begin/DoTransaction()'d and the
        time the server GetTransaction()'s it from it's destination
        Entity; the Servicing phase, between GetTransaction()'ing it,
        and ReplyTransaction()'ing it; and lastly the Response phase,
        which begins when the Transaction is returned to it's Source
        Entity. The most common use of this field is to determine if a
        Transaction obtained from GetTransaction() is a Request from
        another Entity, or merely a returning Transaction that was
        originally sent from this Entity.

    trans_Error

        Both nipc.library and the user may write and examine this
        field. It is cleared (set to ENVOYERR_NOERROR) on a call to
        Do/BeginTransaction().  If the Transaction can be successfully
        copied locally or over the network, any value placed in this
        field by the responder will be here.  If the Transaction
        cannot be copied for some reason, nipc.library will place it's
        error code here.  Common error codes are defined in
        <envoy/errors.h>. Human-understandable text errors can be
        obtained from these error numbers with a call to envoy.library
        [NOT YET AVAILABLE].

    trans_Flags

        This field is a series of nipc.library flags.  These flags
        indicate several things, including who is responsible for
        freeing buffers.  Flags should not be set or cleared by the
        user except through NIPC functions or as permitted by comments
        in the Includes.

    trans_Sequence

        Used internally by nipc.library.  Do not use or examine.

    trans_RequestData

        Pointer to any data needed from the requester by the
        responder. This may be NULL if trans_ReqDataActual is zero,
        indicating that no request data was necessary.  The responder
        should make no changes to data in this buffer!  Programs which
        do will behave differently in networked vs. local
        Transactions. See the GetTransaction() autodoc for more
        details.

    trans_ReqDataLength

        Length of the buffer pointed to by trans_RequestData.  It is
        eventually used by the allocator of the buffer to free the
        buffer.

    trans_ReqDataActual

        Length of data in the buffer pointed to by trans_RequestData
        which will actually be used by the responder.  This allows
        re-used Transaction structures to have large buffers which
        aren't copied across the network if the whole buffer isn't
        filled with data.  This may be NULL if no data is to be sent
        to the responder.

    trans_ResponseData

        Pointer to a buffer into which any reply will be placed by the
        responder.  This may be NULL if trans_RespDataLength is NULL.
        Currently this must be allocated by the requester, either at
        AllocTransaction() time or subsequently prior to
        Do/BeginTransaction().

    trans_RespDataLength

        Length of the buffer pointed to by trans_ResponseData.  It is
        eventually used by the allocator of the buffer to free the
        buffer.  The allocator of the buffer is never the responder.

    trans_RespDataActual

        Length of the data in the buffer pointed to by
        trans_ResponseData actually placed there by the responder.
        This allows the responder to specify that the information it
        is returning is smaller than the buffer provided in order to
        avoid unnecessary copying unused buffer across the network.

    trans_Timeout

        This is the value in seconds that the requester expects as the
        maximum time for the responder to spend processing the
        request. nipc.library will add to this value the maximum time
        it expects the transfer of data between the Entities to take
        (this is variable depending on the distance of the machines
        communicating).  If a Transaction exceeds the total timeout
        and NIPC can abort the Transaction, it will be returned to the
        requester with trans_Error set to ENVOYERR_TIMEOUT.  Though no
        timeout will be used if trans_Timeout is set to zero, most
        Transactions should have a timeout.  If there is no timeout, a
        Transaction received by the responder but not replied (i.e.,
        because the machine crashed before getting to the reply) would
        never be returned (unless the requester called
        AbortTransaction()).  Thus, all programs should specify a
        Timeout other than zero or eventually call AbortTransaction()
        themselves.

        On the requester's side, Transactions are moved around with
        nipc.library functions which look and act much like Exec
        Device I/O functions.  Instead of DoIO(), WaitIO(), CheckIO(),
        AbortIO() and BeginIO(), nipc.library has DoTransaction(),
        WaitTransaction(), CheckTransaction(), AbortTransaction() and
        BeginTransaction().

    DoTransaction(dest_entity, src_entity, transaction)

        an easy-to-use Transaction function.  It initiates a
        Transaction and waits for its completion.  This is a
        synchronous function; it does not return until completion of
        the Transaction.  If the Transaction never completes, this
        call never returns. Non-trivial programs should avoid the use
        of this function in order to continue processing break signals
        or other user input. Use BeginTransaction() and Wait() on the
        Entity's signal bit instead.

    BeginTransaction(dest_entity, src_entity, transaction)

        initiates a Transaction without waiting for completion.  This
        is an asynchronous Transaction; control returns immediately.
        Use GetTransaction() on src_entity to get the replied
        Transaction.

    transaction = GetTransaction(entity)

        Gets replied Transactions off of your source Entity.  You
        should use Wait() or WaitTransaction() rather than
        continuously polling. Once woken up from a
        Wait[Transaction](), you should loop on GetTransaction() while
        it returns something other than NULL to get all Transactions
        off the Entity.

    WaitTransaction(transaction)

        Waits for the completion of a previously initiated
        asynchronous Transaction.  This function will not return
        control until the Transaction has completed (successfully or
        unsuccessfully). Programs should not WaitTransaction() on a
        Transaction that has not actually been sent.

    status = CheckTransaction(transaction)

        Examines a previously queued Transaction, and returns
        information on whether it has completed or not.

    AbortTransaction(transaction)

        attempts to cancel a Transaction.  AbortTransaction() may
        fail; if it succeeds, the Transaction is replied by NIPC
        earlier than it may otherwise have been replied. Programs must
        wait for the reply before actually reusing the Transaction.
        AbortTransaction() takes no action on Transactions which are
        already completed.

        The responder's side of a Transaction uses functions which are
        similar to those that manipulate Exec Ports and Messages.
        Rather than WaitPort(), GetMsg() and ReplyMsg(), there are
        WaitEntity(), GetTransaction() and ReplyTransaction().

    WaitEntity(localentity)

        Waits for a Transaction to arrive at an Entity.  Non-trivial
        programs should avoid using this function in order to continue
        processing break signals or other user input.  Use Wait() on
        the Entity's signal bit instead.

    transaction = GetTransaction(entity)

        Transactions must be removed from Entities on the responder's
        side of a Transaction with a call to GetTransaction().

        Once a responder is done processing a Transaction, it must
        return the Transaction to the requester using the
        ReplyTransaction() function.

        Unlike Exec Ports which can be created manually or with an
        amiga.lib function, Entities must be created and destroyed
        with nipc.library or services.library functions.

    myentity = CreateEntity(tag1, ...)

        creates a new Entity which will can be used for calls to
        WaitEntity() and GetTransaction().  Entities created with
        CreateEntity() must eventually be deleted with DeleteEntity().

    DeleteEntity(entity)

        deletes an Entity created by CreateEntity().  Do not use on
        Entities found with FindEntity() (see below).

        Although the Entity structure is not publicly defined, two
        functions are provided to return some information about a given
        Entity.

    success = GetHostName(entity, stringptr, available)

        provides the name of the host on which a given Entity was
        created.  This is useful to responders that need to know which
        machines they are responding to.

    success = GetEntityName(entity stringptr available)

        provides the name of the Entity.  This is primarily useful to
        responders that need to know the name of the Entities to which
        they are responding.

    remoteentity = FindEntity(host, entityname, src_entity, errptr)

        Finds an Entity created by CreateEntity().  This Entity may be
        used as the destination of a Do/BeginTransaction().  Entities
        found with FindEntity() must eventually be forgotten with a call
        to LoseEntity().

    LoseEntity(entity)

        Forgets about an Entity found with a call to FindEntity().



