An overview of EiffelNet Mechanisms

Update Needed: see definition.

To enable clients and servers to exchange objects, you will have to ensure that they can refer to a common address. At the predefined level this is really the only notion that you need to know, although it is useful to get the bigger picture, in particular the concept of socket (which enables systems to set up communication channels), the various forms of communication (single-machine versus multi-machine, stream versus datagram), the kinds of object structure that may be exchanged, the notion of packet, and how to associate commands with communication events. The following paragraphs review these ideas and the corresponding EiffelNet abstractions.

Establishing a common address

When two systems need to communicate through sockets, they must establish a binding through some common point of reference, called an address. Predictably, the notion of address is one of the important internal abstractions of EiffelNet, although in most cases developers of applications using EiffelNet do not need to manipulate address objects directly.

EiffelNet supports two modes of communication: single-machine and multi-machine. In the single-machine case, the two communicating systems are known to be running on the same machine. In the multi-machine case, also known as the network case, they may be running on different machines, and communication occurs through a network. These two modes clearly require a different binding mechanism.

When a client and a server reside on the same machine, they both have access to that machine's file system. This provides a straightforward binding mechanism: the common address will simply be a "path name", the Unix terminology for the full name of a file in a hierarchically organized file system. In the examples below this file will be /tmp/here (file here in the /tmp directory, conventionally used for temporary files). This file must not exist prior to the communication; it will be created by the socket mechanisms and then removed.

For the network style of communication, this simple device of using a path name is no longer applicable. To define a common address, we may use two elements of information: the name of a machine, and the indication of a port on that machine. More precisely:

  • The port will be identified by an integer. In the examples below port 2000 will be used.
  • The machine may be identified in either of two ways: its host name (the name assigned to the machine when the operating system was first installed on it) or its Internet address, a sequence of numbers separated by periods, such as 127.0.0.1. EiffelNet routines that need an argument identifying a machine will indifferently take a host name or an Internet address, passed in either case as a string. In the examples below the identification will be a host name, given as the string "serverhost".

In network-style client-server communication, the mechanism will be dissymmetric, reflecting the possibility (noted earlier) of a single server catering to many clients. The clients will state both the machine identification of their intended server and the port on which they will talk to that server. The server, however, will only specify the port; this means that it makes itself available to any client that cares to talk to it on that port. This provides some of the essential flexibility of client-server communication, where only one of the partners needs to state beforehand whom it wants to talk to.

Sockets and communication modes

A software system will exchange objects with another by sending them to a socket. Although if you stay at the predefined level you will not need to manipulate sockets explicitly, it is useful to understand this concept and know about the corresponding EiffelNet classes.

You may think of a socket as a communication port; by attaching sockets together you enable communication between the corresponding systems, for example a client and a server:

EiffelNet has been designed so that sockets look very much like files. You send objects to a socket in the same way that you write objects onto a file, and receive objects from a socket in the same way that you read objects from a file. This fundamental commonality is reflected in the inheritance hierarchy of the corresponding classes:

Note that the hierarchy as shown is not complete; in particular the full structure uses classes STREAM (of which the STREAM_ classes are heirs) and DATAGRAM for multiple inheritance . Only the classes below the dotted line are part of EiffelNet; the others are part of EiffelBase, the fundamental data structure and algorithm library of ISE Eiffel [ [[Bibliography|2]] ].

The most important property of this inheritance hierarchy is that it shows how sockets fit within the overall structure. Thanks to the common ancestor IO_MEDIUM, socket classes have most of their features in common with files.

In normal usage, the only socket classes that you will need are four classes appearing at the bottom of the above figure. They correspond to two separate distinctions: single-machine versus multi-machine, and reliable versus unreliable.

On the first distinction:

  • If the communicating systems run on the same machine, you may use one of the UNIX_ classes.
  • For systems that run on different machines, you must use one of the NETWORK_ classes. This will also work if the systems are on the same machine, but less efficiently since communication may go through the network.

The use of the word UNIX_ does not mean that the machine must be running the Unix operating system; rather, it denotes a certain style of client-server communication, the Unix style. (This is comparable to the use of the name UNIX_FILE in EiffelBase, for a class describing files that behave in the Unix style even though they may be implemented on non-Unix machines.)

The second distinction reflects two modes of socket communication: stream communication and datagram communication. Both of these modes support two-way communication between systems, but with different properties:

  • A stream socket, as provided by the STREAM_ classes, provides sequenced communication without any loss or duplication of data. Stream communication is normally synchronous: the sending system waits until it has established a connection to the receiving system and transmitted the data.
  • A datagram socket, as provided by the DATAGRAM_ classes, is asynchronous: the sending system emits its data and does not wait for an acknowledgment. Because the sender is not blocked, this mode is more efficient, but it does not guarantee sequencing, reliability or non-duplication.

Sending and receiving simple values

IO_MEDIUM has all the basic input and output facilities applying to objects of basic types, as also offered in FILE(see the specification of FILE in reference [ [[Bibliography|2]] ]). So you can use sockets to send and receive characters, integers, real numbers in simple or double precision and strings. For example, if the type of`my_socket' is one of the socket classes shown on the preceding figures, any of the following calls will be valid: my_socket.put_string ("Some text") my_socket.read_integer_32 my_last_integer := my_socket.last_integer_32

Since sockets are bidirectional, these instructions may all appear as part of the same class provided you make sure to guarantee proper synchronization between senders and receivers. You may also prefer to specialize certain sockets for sending and others for receiving.

Sending and receiving object structures

In many cases, what you will want to send and receive is not just simple values but non-basic objects (instances of arbitrary classes, having as many fields as needed) and, more generally, entire object structures.

The basic mechanism enabling a system to send objects through EiffelNet is also the basic mechanism for storing objects into a file: class STORABLE from EiffelBase.

Note: Although this discussion uses the class STORABLE, it should be noted that this class will in the future be made obsolete by the classes in the Serialization (SED) subcluster of EiffelBase. You will notice that the EiffelNet examples delivered with EiffelStudio use the classes from the Serialization cluster.

As documented in [ [[Bibliography|2]] ], STORABLE provides features to store and retrieve complete object structures. There are three storage procedures, called under the respective forms struct1.basic_store (iom1) struct1.general_store (iom1) struct1.independent_store (iom1)

Assuming that the type of iom1 is IO_MEDIUM or a conforming type such as FILE or one of the _SOCKET classes, and that the type of struct1 conforms to STORABLE. Note that reference [2] in its original version does not include independent_store, and requires iom to be of type FILE rather than the more general IO_MEDIUM. The current version of EiffelBase, however, supports the more general properties described here.

All three storage procedures have the effect of sending to iom1 (whether a file, a socket or some other IO-medium) a copy of the entire object structure starting at struc1. Together with the retrieval routines seen below, they apply the principle of reference completeness stated in [ [[Bibliography|1]] ] and [ [[Bibliography|2]] ]:

Whenever a routine of class STORABLE stores an object into an external file, it stores with it the dependents of that object. Whenever one of the associated retrieval routines retrieves a previously stored object, it also retrieves all its dependents.

For EiffelNet, of course, "storing" and "retrieving" mean sending and receiving. The rest of this section, which applies to sockets as well as to files, will continue to use the original terminology of storage and retrieval.

The three storage procedures differ in their degree of generality:

  • basic_store will only work if the sending and retrieving are performed by instances of the same system (the same executable module).
  • general_store will work if the sender and retriever are different systems (using the same classes for the objects that they exchange), but these systems must run on machines of the same architecture, or at least of architectures that use the same data representation.
  • independent_store will work in the most general case, with the sender and receiver possibly running on platforms using different data representations.

The penalty for using more general representations is that the data representation (as stored into the file or sent to the socket) will have to include more information. So basic_store uses the most compact representation, and independent_store the most verbose.

The scheme for accessing an object structure produced by one of these three procedures is the following, used in a descendant of class STORABLE: if attached {SOME_EXPECTED_TYPE} retrieved (iom2) as l_temp then -- Retrieved object is of expected type and now attached to `l_temp' -- Proceed with normal processing, typically involving calls of the form `l_temp.some_feature' else -- Retrieved was not attached to an object of SOME_EXPECTED_TYPE end

Here iom2 must be of a type conforming to IO_MEDIUM. The object test using the "attached" syntax checks that the root object of the structure produced by the corresponding call to one of the _store procedures is of a type that conforms to the expected type.

Although there are three separate storage procedures, there is only one retrieval routine, retrieved; the algorithm for retrieved is able to figure out, from the format of the retrieved objects, whether they were produced in the basic, general or independent mode.

Packets

The classes PACKET and DATAGRAM_PACKET are used to represent packets of data that can be sent to sockets.

Their main use is for a system that relies on datagram communication. As noted above, this mode does not guarantee sequencing, making each system responsible for checking that packets arrive in the proper order. This is possible through feature number of class DATAGRAM_PACKET, which gives the number of the current packet.

Associating commands with events

EiffelNet supports a highly asynchronous (and hence efficient) mode of operation by offering mechanisms through which you can specify that a certain action must be executed whenever a certain medium becomes available for reading, writing or handling of special cases (out of bounds). This facility is provided by a set of related classes:

  • The actions are represented by class POLL_COMMAND, an heir of the EiffelBase class COMMAND with, in particular, the procedure execute.
  • Using MEDIUM_POLLER, you can specify that a certain command (an instance of POLL_COMMAND) must be executed whenever a certain medium becomes available for the appropriate operation (read, write, handling of out-of-bounds cases).
  • Using POLL_MASK, you can set a mask to select the sockets or files on which your instance of MEDIUM_POLLER is working.
cached: 03/19/2024 3:44:32.000 AM