Event-driven command execution
9.1 Commands and events
In the preceding examples each participant in a communication had to get ready to send or receive at specific stages of its life. Although this did not preclude asynchronous communication, it is sometimes desirable to make the scheme even more asynchronous, and control more decentralized, by letting each system simply specify certain communication events that it wants to monitor, and certain commands to be executed on occurrence of the specified events.
The commands are objects, instances of a general-purpose class COMMAND
or its proper descendants. Class COMMAND
has, among its features, a procedure
In EiffelNet the possible events associated with a socket will be of three kind: a read event; a write event; or a special event (out of bounds operation). The command classes will be descendants of POLL_COMMAND, an heir of COMMAND
.
9.2 Command classes
The example uses three command classes: DATAGRAM_READER
, used by both clients and servers, and specialized versions of a datagram writer: one for clients, CLIENT_DATAGRAM_WRITER
, and one for servers, SERVER_DATAGRAM_WRITER
. These classes model operations that must be triggered in the case of a read event and a write event.
Here is the common reader command:
class
DATAGRAM_READER
inherit
POLL_COMMAND
redefine
active_medium
end
create
make
feature
active_medium: NETWORK_DATAGRAM_SOCKET
execute (arg: ANY)
local
rec_pack: PACKET
datagram: DATAGRAM_PACKET
i: INTEGER
do
rec_pack := active_medium.received (10, 0)
create datagram.make_from_managed_pointer (rec_pack.data)
io.putint (datagram.packet_number)
io.new_line
from i := 0 until i > 9 loop
io.putchar (datagram.element (i))
i := i + 1
end
io.new_line
end
end -- class DATAGRAM_READER
The class
CLIENT_DATAGRAM_WRITER
inherit
POLL_COMMAND
redefine
active_medium
end
create
make
feature
active_medium: NETWORK_DATAGRAM_SOCKET
execute (arg: ANY)
local
sen_pack: DATAGRAM_PACKET
char: CHARACTER
do
-- Make packet with characters `a' to `j' in successive positions
create sen_pack.make (10)
from char := 'a' until char > 'j' loop
sen_pack.put_element (char, char |-| 'a')
char := char.next
end
sen_pack.set_packet_number (1)
active_medium.send (sen_pack, 0)
end
end -- class CLIENT_DATAGRAM_WRITER
9.3 The server and the client
Once the commands have been defined, it suffices for the server and the client to associate instances of these commands with the appropriate.
The abstraction needed for this purpose is provided by class MEDIUM_POLLER
. An instance of this class knows about a number of commands, each associated with a certain socket in read, write or special event mode. By applying procedure
Here is the server built with this mechanism:
class
POLLING_SERVER
create
make
feature
make (argv: ARRAY [STRING])
local
soc: detachable NETWORK_DATAGRAM_SOCKET
ps: MEDIUM_POLLER
readcomm: DATAGRAM_READER
writecomm: SERVER_DATAGRAM_WRITER
do
if argv.count /= 2 then
io.error.putstring ("Usage: ")
io.error.putstring (argv.item (0))
io.error.putstring (" portnumber%N")
else
create soc.make_bound (argv.item (1).to_integer)
create ps.make_read_only
create readcomm.make (soc)
ps.put_read_command (readcomm)
create writecomm.make (soc)
ps.put_write_command (writecomm)
ps.execute (15, 20000)
ps.make_write_only
ps.execute (15, 20000)
soc.close
end
rescue
if soc /= Void and then not soc.is_closed then
soc.close
end
end
end -- POLLING_SERVER
Procedure POLLING_CLIENT
). Then the server reverses the poller's set-up to write-only, and calls
The client follows the same scheme, reversing the order of read and write operations:
class
POLLING_CLIENT
create
make
feature
make (argv: ARRAY [STRING])
local
soc: detachable NETWORK_DATAGRAM_SOCKET
ps: MEDIUM_POLLER
readcomm: DATAGRAM_READER
writecomm: CLIENT_DATAGRAM_WRITER
do
if argv.count /= 3 then
io.error.putstring ("Usage: ")
io.error.putstring (argv.item (0))
io.error.putstring (" hostname portnumber%N")
else
create soc.make_targeted (argv.item (1), argv.item (2).to_integer)
create ps.make_write_only
create readcomm.make (soc)
ps.put_read_command (readcomm)
create writecomm.make (soc)
ps.put_write_command (writecomm)
ps.execute (15, 20000)
ps.make_read_only
ps.execute (15, 20000)
soc.close
end
rescue
if soc /= Void and then not soc.is_closed then
soc.close
end
end
end
9.4 A less deterministic scheme
Although the example uses the event-driven mechanisms of EiffelNet, it is still relatively deterministic in that it follows a precise protocol defined by a strict sequence of read and write operations on both sides. This is why every call to
A less deterministic scheme may often be desirable, where you simply enter a number of commands (read, write, out of bounds processing) into a poller and then wait for arbitrary events to occur and trigger commands. There is no need with this scheme to know in advance the order in which events may occur: a read event will trigger the command entered into the poller through
To achieve this behavior, simply create the poller using