| Online Eiffel Documentation

EiffelStudio

# Working at predefined level

The example classes discussed in this section appear in the subdirectorypredef of the example 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.

### 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.

### 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.

### 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 configuration 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, seeusing datagram sockets. The server class should inherit from UNIX_SERVER orNETWORK_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.

### 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 proceduremake 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 tomake is the path name that has been selected to enable communication with the server:/tmp/here.
- The client has defined a procedurebuild_list to construct an object structure.
- It then uses the inherited proceduresend 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.
- Thereceive 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 procedurereceive is available through featurereceived. To avoid the need for an assignment attempt (as with the result of aretrieve operation with STORABLE) the class redefinesreceived to be of type STORABLE.
- The client has defined a procedureprocess_received to print the received string.
- Finally, a call tocleanup 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; usemake to specify the socket address,send to send an object structure,receive to receive an object structure,received to access it, andcleanup to clean things up.

There would be one difference for a NETWORK_CLIENT: proceduremake 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 nameserverhost 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.

### 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 isexecute, inherited from the chosen _SERVER class, which executes a loop that will repeatedly callreceive,process_message,respond andclose. This loop is actually an infinite one, which will only terminate if the server is killed (in which case a Rescue clause will execute procedurecleanup); 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 procedureprocess_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 procedurerespond. 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 ofrespond extends thereceived string and returns it. Procedureresend sends an object structure back to the last connected client. It is also possible to have more sophisticated versions ofrespond, 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.