Online Eiffel Documentation |
Documentation Home > Class Libraries > EiffelNet > EiffelNet |
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 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.
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.
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_CLIENTwill be part of one of these systems; OUR_SERVERwill 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.
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:
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.
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 _SERVERclass, 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.
Copyright 1993-2006 Eiffel Software. All rights reserved. |