home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------*/
- /* *
- * $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().
-
-
-
-