Obtaining a finer degree of control
Let us now take a more internal look at the workings of EiffelNet. The two examples that follow have the same behavior as the preceding one; since their text is less simple, they are only interesting as an illustration of the lower-level facilities that you may want to use in specific cases. If you are already familiar with socket programming, they will also give you a more precise idea of how EiffelNet encapsulates the basic socket mechanisms.
As before, we have a client and a server class, still called OUR_CLIENT
and OUR_SERVER
, which are part of two different systems and will run concurrently. The communication uses streams rather than datagrams; the datagram form of communication will be examined in section using datagram sockets .
A client and a server on the same machine
First, let us assume that the client and server run on the same machine, so that we will use the UNIX_ versions of the classes (the next example will be multi-machine). The communication protocol is also the same as before: the client sends a list of strings, the server returns it extended.
Here we will create and manipulate sockets directly. For that reason, both classes inherit from the EiffelNet class SOCKET_RESOURCES
which introduces a number of constants and other useful socket-related features.
The two sockets must be able to refer to a common address. For a communication within a single machine, as noted, this address is a path name, again /tmp/here for this example. The address will be an argument of the creation procedure used to obtain a socket create soc1.make_client ("/tmp/here")
create soc1.make_server ("/tmp/here")
The
Because communication is bidirectional, the distinction between client and server is not between who sends and who receives, although here the server only sends messages of acknowledgment. The client is the party that initiates the communication; the server is the party which stands ready to accept the communication. This difference justifies the presence of two creation procedures soc1.connect
To make itself ready for the communication, the server will execute:
soc1.listen (n)
where
When you use the _SERVER
classes of the predefined level, as in the earlier example, 5 is indeed the default; you can change the value to a positive integer
Whenever the server needs to exchange objects with one of the clients, it obtains access to the socket through the following sequence:
soc1.accept
soc2 := soc1.accepted
... Storage and retrieval operations using soc2 (not soc1) ...
soc2.close
Procedure if attached {SOME_EXPECTED_TYPE} soc2.retrieved as l_temp then
-- soc2.retrieved was attached to an object of the expected type. Now that object is attached to `l_temp'
-- Proceed with normal computation, typically involving calls of the form l_temp.some_feature
else
-- soc2.retrieved was not attached to an object of the expected type.
end
applying to
The operation
At the end of the processing it is necessary to close the original socket
Here is the server class based on these principles. The actual processing has been put aside in a procedure class
OUR_SERVER
inherit
SOCKET_RESOURCES
STORABLE
creation
make
feature
make
-- Accept communication with client and exchange messages
local
count: INTEGER
soc1: detachable UNIX_STREAM_SOCKET
do
create soc1.make_server ("/tmp/here")
from
soc1.listen (5)
count := 0
until
count = 3
loop
process (soc1) -- See below
count := count + 1
end
soc1.cleanup
rescue
if soc1 /= Void then
soc1.cleanup
end
end
process (soc1: UNIX_STREAM_SOCKET)
-- Receive a message, extend it, and send it back
do
soc1.accept
if attached soc1.accepted as l_soc2 then
if attached {OUR_MESSAGE} retrieved (l_soc2) as l_our_new_list then
across
l_our_new_list as it
loop
io.put_string (it.item)
io.new_line
end
l_our_new_list.extend ("%N I'm back.%N")
l_our_new_list.independent_store (l_soc2)
end
soc2.close
end
end
end
Note that at the end the server should not only closes the original socket class
OUR_CLIENT
inherit
SOCKET_RESOURCES
creation
make
feature
make
-- Establish communication with server, and exchange messages
local
soc1: detachable UNIX_STREAM_SOCKET
do
create soc1.make_client ("/tmp/here")
soc1.connect
process (soc1) -- See below
soc1.cleanup
rescue
if soc1 /= Void then
soc1.cleanup
end
end
process (soc1: UNIX_STREAM_SOCKET)
-- Build a message to server, receive answer, build
-- modified message from that answer, and print it.
local
our_list: OUR_MESSAGE
do
create our_list.make
our_list.extend("This")
our_list.extend (" is")
our_list.extend (" our")
our_list.extend (" test")
our_list.independent_store (soc1)
if attached {OUR_MESSAGE} our_list.retrieved (soc1) as l_our_new_list then
across
l_our_new_list as it
loop
io.putstring (it.item)
end
io.new_line
end
end
end
Communication between two different machines
Let us now assume that the client and the server will run on two separate machines. Instead of UNIX_ sockets, we must now use sockets of type NETWORK_STREAM_SOCKET
.
The available creation procedures are slightly different. The server will be set up so as to listen to clients from any machine; it designates a port, identified by an integer, on which it will listen. The socket creation on the server side is then
create soc1.make_server_port (2000)
For the client, the creation will specify two elements of information: the port number and the server. The server argument, a string, identifies the machine used as a server; it may be the host name of that machine, for example "serverhost" as used in the example; or it can be the machine's internet address, made of a sequence of numbers separated by periods, such as "127.0.0.1".
The rest of the classes is as before.
class
OUR_SERVER
inherit
SOCKET_RESOURCES
STORABLE
creation
make
feature
soc1: detachable NETWORK_STREAM_SOCKET
make
-- Accept communication with client and exchange messages.
do
create soc1.make_server_by_port (2000)
... The rest as before...
end
process
... As before ...
end
end
class
OUR_CLIENT
inherit
SOCKET_RESOURCES
creation
make
feature
soc1: detachable NETWORK_STREAM_SOCKET
make
do
create soc1.make_client_by_port (2000, "serverhost")
... The rest as before ...
end
process
... As before ...
end
end