This site contains older material on Eiffel. For the main Eiffel page, see http://www.eiffel.com.

 EIFFELNET MANUAL 

6 WORKING AT THE PREDEFINED LEVEL

The example classes discussed in this section appear in the subdirectory predef of the examples directory.

The first example shows how to use the predefined level classes, covering many common client-server schemes. The work needed in this case is minimal: just effect or redefine a few routines describing the specific processing that your client and your server require.

6.1 The example

The example describes a simple communication protocol. The client sends to the server a list of strings, which together form the message "This is my test"; the server receives the corresponding object structure, appends to it one more element - the string "I'm back" - and returns the result to the client, which will accept it and print it. The server increases a counter each time it closes a connection with a client, and will only accept three connections.

6.2 Object structures

As noted above, it is possible with sockets, as any other IO_MEDIUM, to send and receive simple objects such as integers. But for this first example we are already more ambitious and want to exchange entire linked lists of strings. The structures that we will exchange are described by the following class:

    class OUR_MESSAGE inherit
    LINKED_LIST [STRING];
    STORABLE
    creation
    make
    end

Note that to make use of the storage and retrieval facilities the objects to be exchanged must be instances of a class which, as here, is a descendant of STORABLE.

6.3 Application classes and system structure

The client and server mechanisms will be described by instances of two classes that make up the example, called OUR_CLIENT and OUR_SERVER.

We want to have the two communicating systems be two different systems (compiled separately, with two separate Ace files). OUR_CLIENT will be part of one of these systems; OUR_SERVER will be part of the other.

There are four predefined classes, corresponding to the client and server sides with, in each case, the choice between the "Unix" (single-machine) and "network" (multi-machine) styles of communication. Note that all these classes support stream communication; for datagrams, see chapter 8. The server class should inherit from UNIX_SERVER or NETWORK_SERVER; the client class should inherit from UNIX_CLIENT or NETWORK_CLIENT. The choice between UNIX_ and NETWORK_ classes must of course be the same on both sides.

6.4 The client

Here is the client class:

    class OUR_CLIENT inherit
    UNIX_CLIENT
    redefine received end
    creation
    make_client
    feature
    our_list: OUR_MESSAGE;

    received: OUR_MESSAGE; -- Type redefinition

    make_client is
    -- Build list, send it, receive modified list, and print it.
    do
    make ("/tmp/here");
    build_list;
    send (our_list);
    receive;
    process_received;
    cleanup
    rescue
    cleanup
    end;


    build_list is
    -- Build list of strings our_list for transmission to server.
    do
    create our_list.make; our_list.extend ("This "); our_list.extend ("is ");
    our_list.extend ("our "); our_list.extend ("test")
    end


    process_received is
    -- Print the contents of received in sequence.
    do
    if received = Void then
    io.putstring ("No list received")
    else
    from received.start until received.after loop
    io.putstring (received.item);
    received.forth
    end
    end;
    io.new_line
    end
    end

The scheme is very simple:

    The inherited procedure make takes care of the many details of creating a socket and setting up the communication; the following sections will give some insight into what this involves. The argument to make is the path name that has been selected to enable communication with the server: /tmp/here.

    The client has defined a procedure build_list to construct an object structure.

    It then uses the inherited procedure send to send this object structure to the server; send expects an argument of a type conforming to STORABLE, so that it can use all the predefined storage and retrieval facilities.

    The receive procedure, called next, obtains an object structure from the server; here, as will be seen from the server's text below, this is the original list to which the server has appended a new element. The result of procedure receive is available through feature received. To avoid the need for an assignment attempt (as with the result of a retrieve operation with STORABLE) the class redefines received to be of type STORABLE.

    The client has defined a procedure process_received to print the received string.

    Finally, a call to cleanup is necessary to close the sockets that have been opened and free the corresponding resources.

Of course, in a typical object-oriented design, the class and those which appear in the rest of this set of examples may "do" more than one thing and so may have more routines than the ones shown in this document.

This example shows how to write a client class in the most common cases: inherit from UNIX_CLIENT or NETWORK_CLIENT; use make to specify the socket address, send to send an object structure, receive to receive an object structure, received to access it, and cleanup to clean things up.

There would be one difference for a NETWORK_CLIENT: procedure make would need two arguments, the name of a host and a port on that host. For example, to connect to a server running on a machine of name serverhost and using port 2000, the call would be:

    make ("serverhost", 2000)

As noted earlier, you may identify the machine by an Internet address such as "127.0.0.1" rather than by a host name.

6.5 The server

Now, the server class:

    class OUR_SERVER inherit
    UNIX_SERVER
    redefine received, respond end
    creation
    make_server
    feature
    received: OUR_MESSAGE

    make_server is
    -- Receive list, print it, extend it, and send the result back.
    do
    make ("/tmp/here");
    execute
    rescue
    cleanup
    end;


    process_message is
    -- Print received list.
    do
    from received.start until received.after loop
    io.putstring (received.item);
    received.forth
    end;
    io.new_line
    end;


    respond is
    -- Extend received list.
    do
    received.extend ("%N I'm back.%N");
    resend (received)
    end
    end

The developer's task is even easier here than on the client side. The last instruction in the creation procedure is execute, inherited from the chosen _SERVER class, which executes a loop that will repeatedly call receive, process_message, respond and close. This loop is actually an infinite one, which will only terminate if the server is killed (in which case a Rescue clause will execute procedure cleanup); indeed, a server is not expected to terminate under normal circumstances.

To define a server's behavior as desired for your particular application, all you are required to do is to effect the procedure process_message, inherited from the _SERVER class in deferred form. (Remember that to "effect" a deferred feature is to provide a non-deferred version, also called an effective version.) Here the effective version prints the list of strings received from the client.

In addition, you may also, as here, redefine procedure respond. In the _SERVER class this procedure is effective, but it does nothing; this means that you have nothing special to do for the common case in which the server does not respond when it has received a message. Here, however, we do want to respond; the redefined version of respond extends the received string and returns it. Procedure resend sends an object structure back to the last connected client. It is also possible to have more sophisticated versions of respond, for example to dispatch objects to all active clients; this will be shown in a later example

As in the client case there is a cleanup procedure to free the sockets, but you do not need to call it explicitly; it is called by execute. You may redefine this procedure to free some additional resources of your own.

PreviousPrevious Chapter TOCTable of Contents NextNext Chapter