created 20.11

git-svn-id: https://svn.eiffel.com/eiffel-org/trunk@2267 abb3cda0-5349-4a8f-a601-0c33ac3a8c38
This commit is contained in:
eifops
2020-12-04 15:34:39 +00:00
parent 0f1156d9cc
commit 5049d0d511
2944 changed files with 62467 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
[[Property:title|EiffelNet Class Reference]]
[[Property:weight|1]]
[[Property:uuid|25ab1fde-b4bc-2f45-1ec2-6dd109bdfc39]]
==View the [[ref:libraries/net/reference/index|EiffelNet Class Reference]]==

View File

@@ -0,0 +1,29 @@
[[Property:title|Advanced]]
[[Property:weight|0]]
[[Property:uuid|9229e5c1-165e-add7-6ce2-355dec4544f1]]
==Compiling==
To compile the example, you must compile two projects: ''chat'' and ''join''.
To compile the ''chat ''project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\advanced\chat\''.
* Choose ''chat.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.
To compile the ''join ''project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\advanced\join\''.
* Choose ''join.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.

View File

@@ -0,0 +1,7 @@
[[Property:title|EiffelNet Samples]]
[[Property:weight|2]]
[[Property:uuid|c9c0fe01-a80f-5834-8503-aad1063e1503]]
=Description=
Here is a set of five examples written to show some basic applications using the EiffelNet library . Two of them (Advanced and Same Machine) rely on specific code to Unix Operating systems and consequently cannot be run under Windows.

View File

@@ -0,0 +1,26 @@
[[Property:title|Polling]]
[[Property:weight|1]]
[[Property:uuid|3bdc742f-2214-c5fb-2336-bc613b721c0d]]
==Compiling==
To compile the example, you must compile two projects: ''client'' and ''server.''
To compile the ''client'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\polling\client\''.
* Choose ''polling_client.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.
To compile the ''server'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\polling\server\''.
* Choose ''polling_server.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.

View File

@@ -0,0 +1,26 @@
[[Property:title|Predef]]
[[Property:weight|2]]
[[Property:uuid|d9851197-ae39-5e73-9bf3-51d7cb9fa23c]]
==Compiling==
To compile the example, you must compile two projects: ''client'' and ''server.''
To compile the ''client'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\predef\client\''.
* Choose ''our_client.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.
To compile the ''server'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\predef\server\''.
* Choose ''our_server.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.

View File

@@ -0,0 +1,26 @@
[[Property:title|Same Machine]]
[[Property:weight|3]]
[[Property:uuid|d10e1e4b-94c1-2725-f465-79da88730196]]
==Compiling==
To compile the example, you must compile two projects: ''client'' and ''server.''
To compile the ''client'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\same_mach\client\''.
* Choose ''our_client.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.
To compile the ''server'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\same_mach\server\''.
* Choose ''our_server.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.

View File

@@ -0,0 +1,27 @@
[[Property:title|Two Machines]]
[[Property:weight|4]]
[[Property:uuid|52307bfe-116d-f5c4-ccf1-98f330ef82e5]]
==Compiling==
To compile the example, you must compile two projects: ''client'' and ''server.''
To compile the ''client'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\two_mach\client\''.
* Choose ''our_client.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.
To compile the ''server'' project:
* Launch EiffelStudio.
* Click '''Add project'''
* Browse to ''$ISE_EIFFEL\examples\net\two_mach\server\''.
* Choose ''our_server.ecf''
* Choose the location where the project will be compiled, by default the same directory containing the configuration file.
* Click '''OK'''.

View File

@@ -0,0 +1,16 @@
[[Property:title|Bibliography]]
[[Property:weight|10]]
[[Property:uuid|739d59a6-c0b0-899f-24cf-5a17655a5512]]
[1] Bertrand Meyer: [[Eiffel: The Language]], Prentice Hall, 1992. (Available from Eiffel Software.)
[2] Bertrand Meyer: [http://www.eiffel.com/services/training/books.html Reusable Software: The Base Object-Oriented Component Libraries], Prentice Hall, 1994. (Available from Eiffel Software.)
[3] W. Richard Stevens: ''Unix Network Programming'', Prentice Hall, 1990.

View File

@@ -0,0 +1,19 @@
[[Property:title|Clients and servers]]
[[Property:weight|1]]
[[Property:uuid|3c1711f8-3aa0-9f3b-c1ce-0cd9324b3e00]]
In the client-server model of computing, a number of software systems, the clients, may require general-purpose services provided by other systems, the servers. Clients and servers run concurrently, on the same machine or, more generally, on different machines connected through a network.
Typically, the clients are systems, for example systems that provide useful facilities for human users; the servers take care of operations that clients could not perform on their own (or would perform less efficiently), and of operations that are common to several clients. Example of servers include:
* '''Computation servers''', which take care of heavy or specialized computations not appropriate for the clients, assuming the servers' processors are more powerful.
* '''File servers''' and '''database servers''', providing access to persistent data that different clients may need to access.
The client-server relation is usually many-to-many: each client application, in the course of performing its duties, may successively require services from different servers; and each server may cater to different clients. This second aspect, illustrated by the following figure, is particularly important; it allows a single server to communicate with as many clients as appropriate.
[[Image:fig-1]]
Although communication occurs both ways - clients sending objects to servers and conversely - the situation in such a scheme is usually not symmetric. Whereas a client requesting a certain service will need to know beforehand what server provides that service, the server, for its part, will not in general name its clients in advance; it will simply stand ready to provide its services to whatever suitable client happens to request them.
This dissymmetry appears clearly in EiffelNet and in the examples of this manual. It means in particular that clients and servers act differently at the '''beginning''' of their client-server lifecycle. The party that initiates the communication is the client; in this operation it will identify the desired server (through a local file name for communication on the same machine, or though a machine name for network communication). The server simply makes itself ready for possible connections by starting to "listen" on the communication channels - relying for this on an EiffelNet procedure that is indeed called ''listen''.

View File

@@ -0,0 +1,207 @@
[[Property:title|Event-driven command execution]]
[[Property:weight|8]]
[[Property:uuid|66036773-3e41-2877-7fa4-e0601558fc7c]]
{{note|The example classes discussed in this section appear in the subdirectory ''polling ''of the example directory.}}
===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 <code>COMMAND</code> or its proper descendants. Class <code>COMMAND</code> has, among its features, a procedure <eiffel>execute</eiffel> which executes the current command; some commands are undoable and have an <eiffel>undo</eiffel> 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 [[ref:libraries/net/reference/poll_command_chart|POLL_COMMAND]], an heir of <code>COMMAND</code>.
===9.2 Command classes===
The example uses three command classes: <code>DATAGRAM_READER</code>, used by both clients and servers, and specialized versions of a datagram writer: one for clients, <code>CLIENT_DATAGRAM_WRITER</code>, and one for servers, <code>SERVER_DATAGRAM_WRITER</code>. 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:
<code>
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
</code>
The <eiffel>execute</eiffel> procedure reads a packet of ten characters and prints these characters. Its counterpart in the writing command will produce these ten packets:
<code>
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
</code>
===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 <code>MEDIUM_POLLER</code>. 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 <eiffel>execute</eiffel> to such a medium poller, you direct it to monitor these sockets for the corresponding events and to execute the command associated with each event that will be received. Procedure <eiffel>execute</eiffel> takes two integer arguments: the maximum number of sockets to monitor, and the timeout in milliseconds.
Here is the server built with this mechanism:
<code>
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
</code>
Procedure <eiffel>make</eiffel> creates three objects: a socket, which it associates with a specific port; a poller; and a read command (an instance of DATAGRAM_READER), which it attaches to the socket. It then enters the read command into the poller, and does the same thing with a write command. It sets up the poller to accept read commands only and then executes the poller; this will enable the server to get the read event triggered by the client's write command (as it appears below in the text of class <code>POLLING_CLIENT</code>). Then the server reverses the poller's set-up to write-only, and calls <eiffel>execute</eiffel> again.
The client follows the same scheme, reversing the order of read and write operations:
<code>
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
</code>
===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 <eiffel>execute</eiffel> is preceded by a call to either <eiffel>make_read_only</eiffel> or <eiffel>make_write_only</eiffel> to set up the poller in the appropriate mode.
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 <eiffel>put_read_command</eiffel>; a write event will trigger the command entered through <eiffel>put_write_command</eiffel>.
To achieve this behavior, simply create the poller using <eiffel>make</eiffel> as creation procedure. This will set up the poller so as to accept all socket events, and enter into event-driven command execution by calling <eiffel>execute</eiffel> on the poller.

View File

@@ -0,0 +1,5 @@
[[Property:title|EiffelNet Tutorial]]
[[Property:weight|0]]
[[Property:uuid|09679e98-4d5c-d2af-439b-00b28c6cfe4b]]

View File

@@ -0,0 +1,40 @@
[[Property:title|Introduction to the examples]]
[[Property:weight|4]]
[[Property:uuid|3d9d54ab-0324-d8d4-ae32-f5379f2fc721]]
The following sections describe a set of examples showing how to use EiffelNet to build client-server applications.
The examples start with the most simple uses involving high-level classes covering common cases and shielding developers from details of the mechanism. Subsequent examples will reveal some of these details, useful for more advanced or specific applications of EiffelNet.
All the examples discussed here appear in the directory ''$ISE_EIFFEL/examples/net'' of the Eiffel distribution.
===Object structures===
As noted above, it is possible with sockets, as any other <code>IO_MEDIUM</code>, 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:
<code>
class
OUR_MESSAGE
inherit
LINKED_LIST [STRING]
STORABLE
undefine
is_equal, copy
end
create
make
end
</code>
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 <code>STORABLE</code>.
{{caution|On Windows, the examples `advanced` and the `same_mach` are nonfunctional. This is because these examples use code specific to Unix Operating systems. }}

View File

@@ -0,0 +1,17 @@
[[Property:title|A more complex example]]
[[Property:weight|9]]
[[Property:uuid|a57bbf5b-d877-8a50-1bca-92848868bdec]]
{{note|The example classes discussed in this section appear in the subdirectory ''advanced ''of the example directory.}}
The last example exercises most of EiffelNet's major facilities. It consists of a server that allows an arbitrary number of clients to connect to it. Each time the user of one of the client systems types a line on the keyboard, the client sends this character to the server, which then broadcasts it to all the clients (including the original sender). This scheme allows several people to talk together, hence the names chosen: the server class is called <code>CHAT</code>, and the client is called <code>JOIN</code>.
The example uses the network mode of communication, based on the [[ref:libraries/net/reference/network_client_chart|NETWORK_CLIENT]] and [[ref:libraries/net/reference/network_server_chart|NETWORK_SERVER ]] classes. It uses automatic polling through [[ref:libraries/net/reference/medium_poller_chart|MEDIUM_POLLER]] as in the previous example; the relevant command is given by class <code>CONNECTION</code>, an heir of [[ref:libraries/net/reference/poll_command_chart|POLL_COMMAND]] . The information exchanged between the server and its clients is described by class <code>MESSAGE</code>, an heir of [[ref:libraries/base/reference/linked_list_chart|LINKED_LIST]] [ [[ref:libraries/base/reference/string_8_chart|STRING]] ] similar to the earlier examples' OUR_MESSAGE (see [[Introduction to the examples]] ). Attributes include, the name <eiffel>client_name</eiffel> of the client that has sent this message, the boolean <eiffel>new</eiffel> indicating whether the current message is the first from a client that is trying to connect to the server, and <eiffel>over</eiffel> indicating that the message is the last sent by a client before disconnecting.
The server maintains a list of the currently active connections. In the <eiffel>receive</eiffel> routine, it checks on the main socket for any client trying to connect. The socket is set to be non-blocking to enable the server to continue checking the already connected clients. If the connection is successful, the server sends to the new client the list of clients already connected and adds the new connection to its list. Then it polls the connections in the list, and processes the messages, if any. If the message is tagged <eiffel>new</eiffel>, the server sends a message to all the clients indicating that a new client has joined the server; if it is tagged <eiffel>over</eiffel>, it sends a message indicating that the client has opted out.
Each client uses the [[ref:libraries/net/reference/medium_poller_chart|MEDIUM_POLLER]] to check any message coming from the server and immediately displays any such message. It also checks a special connection, created with <eiffel>io.input</eiffel> as a medium, to check what the user is typing and then send it to the server. If the user types ''bye'', the client terminates, sending a message tagged <eiffel>over</eiffel> to the server.

View File

@@ -0,0 +1,253 @@
[[Property:title|Obtaining a finer degree of control]]
[[Property:link_title|]]
[[Property:weight|6]]
[[Property:uuid|9e2de24b-8ae3-5b57-9797-e163defe83d9]]
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 <code>OUR_CLIENT</code> and <code>OUR_SERVER</code>, 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|using datagram sockets]] .
===A client and a server on the same machine===
{{note|The example classes discussed in this section appear in the subdirectory ''same_mach'' of the example directory }}
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 <code>SOCKET_RESOURCES</code> 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 <eiffel>soc1</eiffel> on either side:
<code>
create soc1.make_client ("/tmp/here")
create soc1.make_server ("/tmp/here")
</code>
The <eiffel>make_</eiffel> procedures take care of all the hassles of establishing a socket for a client or a server: creating an address object, setting it to the given path name, binding the socket to the address, and in the client case establishing the connection. For finer control, these procedures are still available: you can create a bare socket by using the basic creation procedure <eiffel>make</eiffel> (rather than the more sophisticated <eiffel>make_client</eiffel>
and <eiffel>make_server</eiffel>), then create a separate address object, associate the two, and call the <eiffel>bind</eiffel> and <eiffel>connect</eiffel> procedures.
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 <eiffel>make_client</eiffel> and <eiffel>make_server</eiffel> as illustrated above. To initiate the communication, the client will execute:
<code>
soc1.connect</code>
To make itself ready for the communication, the server will execute:
<code>
soc1.listen (n)</code>
where <eiffel>n</eiffel> is a positive integer indicating the maximum number of client connection attempts that may be queued. The value 5, used in the example, is typical for <eiffel>n</eiffel>.
When you use the <code>_SERVER </code>classes of the predefined level, as in the earlier example, 5 is indeed the default; you can change the value to a positive integer <eiffel>n</eiffel> through the call <eiffel>set_queued (n)</eiffel> .
Whenever the server needs to exchange objects with one of the clients, it obtains access to the socket through the following sequence:
<code>
soc1.accept
soc2 := soc1.accepted
... Storage and retrieval operations using soc2 (not soc1) ...
soc2.close
</code>
Procedure <eiffel>accept</eiffel> ensures synchronization with the client. When communication is established, <eiffel>accept</eiffel> creates a new socket which will be accessible through attribute <eiffel>accepted</eiffel>, whose value is here assigned to the local entity <eiffel>soc2</eiffel>. To receive objects, the server will use operations of the form introduced earlier ( [[An overview of EiffelNet Mechanisms|An overview of EiffelNet, sending and receiving object structures]] ):
<code>
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
</code>
applying to <eiffel>soc2</eiffel>, not <eiffel>soc1</eiffel>; this makes <eiffel>soc1</eiffel> available to accept connections with other clients, a fundamental feature of client-server mechanisms.
The operation <eiffel>soc2.close</eiffel> which terminates the above sequence closes the new socket. In principle this is not necessary, since garbage collection should eventually reclaim the socket object, and the <eiffel>dispose</eiffel> procedure of the corresponding socket class includes a call to <eiffel>close</eiffel>. But the risk exists that you run out of sockets before garbage collection reclaims all currently opened sockets, so it is preferable to include the <eiffel>close</eiffel> calls explicitly.
At the end of the processing it is necessary to close the original socket <eiffel>soc1</eiffel> but also to unlink it. The feature <eiffel>cleanup</eiffel> from class <eiffel>SOCKET</eiffel> takes care of both closing and unlinking.
Here is the server class based on these principles. The actual processing has been put aside in a procedure <eiffel>process</eiffel>.
<code>
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
</code>
Note that at the end the server should not only closes the original socket <eiffel>soc1</eiffel> but also unlinks it. It is recommended to have a Rescue clause which, as here, ensures that the socket will be closed and unlinked if the system terminates abnormally before its term.
Here now is the client class:
<code>
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
</code>
===Communication between two different machines===
{{note|The example classes discussed in this section appear in the subdirectory ''two_mach'' of the example directory }}
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 <code>NETWORK_STREAM_SOCKET</code>.
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
<code>
create soc1.make_server_port (2000)</code>
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.
<code>
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
</code>

View File

@@ -0,0 +1,130 @@
[[Property:title|An overview of EiffelNet Mechanisms]]
[[Property:weight|2]]
[[Property:uuid|08d7ca19-65e3-7f35-fbce-a50d0aedf626]]
{{UpdateNeeded}}
To enable clients and servers to exchange objects, you will have to ensure that they can refer to a common '''address'''. At the predefined level this is really the only notion that you need to know, although it is useful to get the bigger picture, in particular the concept of '''socket '''(which enables systems to set up communication channels), the various forms of communication (single-machine versus multi-machine, stream versus datagram), the kinds of object structure that may be exchanged, the notion of packet, and how to associate commands with communication events. The following paragraphs review these ideas and the corresponding EiffelNet abstractions.
===Establishing a common address===
When two systems need to communicate through sockets, they must establish a binding through some common point of reference, called an '''address'''. Predictably, the notion of address is one of the important internal abstractions of EiffelNet, although in most cases developers of applications using EiffelNet do not need to manipulate address objects directly.
EiffelNet supports two modes of communication: single-machine and multi-machine. In the single-machine case, the two communicating systems are known to be running on the same machine. In the multi-machine case, also known as the '''network''' case, they may be running on different machines, and communication occurs through a network. These two modes clearly require a different binding mechanism.
When a client and a server reside on the same machine, they both have access to that machine's file system. This provides a straightforward binding mechanism: the common address will simply be a "path name", the Unix terminology for the full name of a file in a hierarchically organized file system. In the examples below this file will be ''/tmp/here ''(file ''here'' in the ''/tmp'' directory, conventionally used for temporary files). This file must not exist prior to the communication; it will be created by the socket mechanisms and then removed.
For the network style of communication, this simple device of using a path name is no longer applicable. To define a common address, we may use two elements of information: the name of a machine, and the indication of a '''port''' on that machine. More precisely:
* The port will be identified by an integer. In the examples below port ''2000'' will be used.
* The machine may be identified in either of two ways: its '''host name''' (the name assigned to the machine when the operating system was first installed on it) or its '''Internet address''', a sequence of numbers separated by periods, such as ''127.0.0.1''. EiffelNet routines that need an argument identifying a machine will indifferently take a host name or an Internet address, passed in either case as a string. In the examples below the identification will be a host name, given as the string ''"serverhost"''.
In network-style client-server communication, the mechanism will be dissymmetric, reflecting the possibility (noted earlier) of a single server catering to many clients. The clients will state both the machine identification of their intended server and the port on which they will talk to that server. The server, however, will only specify the port; this means that it makes itself available to any client that cares to talk to it on that port. This provides some of the essential flexibility of client-server communication, where only one of the partners needs to state beforehand whom it wants to talk to.
===Sockets and communication modes===
A software system will exchange objects with another by sending them to a socket. Although if you stay at the predefined level you will not need to manipulate sockets explicitly, it is useful to understand this concept and know about the corresponding EiffelNet classes.
You may think of a socket as a communication port; by attaching sockets together you enable communication between the corresponding systems, for example a client and a server:
[[Image:fig-2]]
EiffelNet has been designed so that sockets look very much like files. You send objects to a socket in the same way that you write objects onto a file, and receive objects from a socket in the same way that you read objects from a file. This fundamental commonality is reflected in the inheritance hierarchy of the corresponding classes:
[[Image:fig-3]]
Note that the hierarchy as shown is not complete; in particular the full structure uses classes STREAM (of which the <code> STREAM_ </code> classes are heirs) and <code> DATAGRAM </code> for multiple inheritance ''. ''Only the classes below the dotted line are part of EiffelNet; the others are part of EiffelBase, the fundamental data structure and algorithm library of ISE Eiffel [ [[Bibliography|2]] ].
The most important property of this inheritance hierarchy is that it shows how sockets fit within the overall structure. Thanks to the common ancestor <code>IO_MEDIUM</code>, socket classes have most of their features in common with files.
In normal usage, the only socket classes that you will need are four classes appearing at the bottom of the above figure. They correspond to two separate distinctions: single-machine versus multi-machine, and reliable versus unreliable.
On the first distinction:
* If the communicating systems run on the same machine, you may use one of the <code>UNIX_ </code> classes.
* For systems that run on different machines, you must use one of the <code>NETWORK_ </code> classes. This will also work if the systems are on the same machine, but less efficiently since communication may go through the network.
The use of the word <code>UNIX_ </code> does not mean that the machine must be running the Unix operating system; rather, it denotes a certain style of client-server communication, the Unix style. (This is comparable to the use of the name <code>UNIX_FILE</code> in EiffelBase, for a class describing files that behave in the Unix style even though they may be implemented on non-Unix machines.)
The second distinction reflects two modes of socket communication: stream communication and datagram communication. Both of these modes support two-way communication between systems, but with different properties:
* A stream socket, as provided by the <code>STREAM_</code> classes, provides sequenced communication without any loss or duplication of data. Stream communication is normally synchronous: the sending system waits until it has established a connection to the receiving system and transmitted the data.
* A datagram socket, as provided by the <code> DATAGRAM_ </code> classes, is asynchronous: the sending system emits its data and does not wait for an acknowledgment. Because the sender is not blocked, this mode is more efficient, but it does not guarantee sequencing, reliability or non-duplication.
===Sending and receiving simple values===
<code>IO_MEDIUM</code> has all the basic input and output facilities applying to objects of basic types, as also offered in FILE(see the specification of<code> FILE</code> in reference [ [[Bibliography|2]] ]). So you can use sockets to send and receive characters, integers, real numbers in simple or double precision and strings. For example, if the type of
`my_socket' is one of the socket classes shown on the preceding figures, any of the following calls will be valid:
<code>
my_socket.put_string ("Some text")
my_socket.read_integer_32
my_last_integer := my_socket.last_integer_32
</code>
Since sockets are bidirectional, these instructions may all appear as part of the same class provided you make sure to guarantee proper synchronization between senders and receivers. You may also prefer to specialize certain sockets for sending and others for receiving.
===Sending and receiving object structures===
In many cases, what you will want to send and receive is not just simple values but non-basic objects (instances of arbitrary classes, having as many fields as needed) and, more generally, entire object structures.
The basic mechanism enabling a system to send objects through EiffelNet is also the basic mechanism for storing objects into a file: class <code>STORABLE</code> from EiffelBase.
{{note|Although this discussion uses the class <code>STORABLE</code>, it should be noted that this class will in the future be made obsolete by the classes in the Serialization (SED) subcluster of EiffelBase. You will notice that the EiffelNet examples delivered with EiffelStudio use the classes from the Serialization cluster.}}
As documented in [ [[Bibliography|2]] ], <code>STORABLE</code> provides features to store and retrieve complete object structures. There are three storage procedures, called under the respective forms
<code>
struct1.basic_store (iom1)
struct1.general_store (iom1)
struct1.independent_store (iom1)
</code>
Assuming that the type of ''iom1 ''is <code>IO_MEDIUM</code> or a conforming type such as [[ref:libraries/base/reference/file_chart|FILE]] or one of the <code>_SOCKET</code> classes, and that the type of ''struct1'' conforms to <code>STORABLE</code>. Note that reference [2] in its original version does not include ''independent_store'', and requires ''iom'' to be of type FILE rather than the more general <code>IO_MEDIUM</code>. The current version of EiffelBase, however, supports the more general properties described here.
All three storage procedures have the effect of sending to ''iom1 ''(whether a file, a socket or some other IO-medium) a copy of the entire object structure starting at ''struc1''. Together with the retrieval routines seen below, they apply the principle of reference completeness stated in [ [[Bibliography|1]] ] and [ [[Bibliography|2]] ]:
{| border="1"
|-
| Whenever a routine of class <code>STORABLE</code> stores an object into an external file, it stores with it the dependents of that object.
| Whenever one of the associated retrieval routines retrieves a previously stored object, it also retrieves all its dependents.
|}
For EiffelNet, of course, "storing" and "retrieving" mean sending and receiving. The rest of this section, which applies to sockets as well as to files, will continue to use the original terminology of storage and retrieval.
The three storage procedures differ in their degree of generality:
* ''basic_store'' will only work if the sending and retrieving are performed by instances of the same system (the same executable module).
* ''general_store'' will work if the sender and retriever are different systems (using the same classes for the objects that they exchange), but these systems must run on machines of the same architecture, or at least of architectures that use the same data representation.
* ''independent_store'' will work in the most general case, with the sender and receiver possibly running on platforms using different data representations.
The penalty for using more general representations is that the data representation (as stored into the file or sent to the socket) will have to include more information. So ''basic_store ''uses the most compact representation, and ''independent_store'' the most verbose.
The scheme for accessing an object structure produced by one of these three procedures is the following, used in a descendant of class <code>STORABLE</code>:
<code>
if attached {SOME_EXPECTED_TYPE} retrieved (iom2) as l_temp then
-- Retrieved object is of expected type and now attached to `l_temp'
-- Proceed with normal processing, typically involving calls of the form `l_temp.some_feature'
else
-- Retrieved was not attached to an object of SOME_EXPECTED_TYPE
end
</code>
Here ''iom2'' must be of a type conforming to <code>IO_MEDIUM</code>. The [[ET: Inheritance#Object test|object test]] using the "attached" syntax checks that the root object of the structure produced by the corresponding call to one of the ''_store'' procedures is of a type that conforms to the expected type.
Although there are three separate storage procedures, there is only one retrieval routine, ''retrieved''; the algorithm for ''retrieved ''is able to figure out, from the format of the retrieved objects, whether they were produced in the basic, general or independent mode.
===Packets===
The classes <code>PACKET</code> and <code>DATAGRAM_PACKET</code> are used to represent packets of data that can be sent to sockets.
Their main use is for a system that relies on datagram communication. As noted above, this mode does not guarantee sequencing, making each system responsible for checking that packets arrive in the proper order. This is possible through feature ''number ''of class <code>DATAGRAM_PACKET</code>, which gives the number of the current packet.
===Associating commands with events===
EiffelNet supports a highly asynchronous (and hence efficient) mode of operation by offering mechanisms through which you can specify that a certain action must be executed whenever a certain medium becomes available for reading, writing or handling of special cases (out of bounds). This facility is provided by a set of related classes:
* The actions are represented by class <code>POLL_COMMAND</code>, an heir of the EiffelBase class <code>COMMAND</code> with, in particular, the procedure ''execute''.
* Using <code>MEDIUM_POLLER</code>, you can specify that a certain command (an instance of <code>POLL_COMMAND</code>) must be executed whenever a certain medium becomes available for the appropriate operation (read, write, handling of out-of-bounds cases).
* Using <code>POLL_MASK</code>, you can set a mask to select the sockets or files on which your instance of <code>MEDIUM_POLLER</code> is working.

View File

@@ -0,0 +1,14 @@
[[Property:title|Using datagram sockets]]
[[Property:weight|7]]
[[Property:uuid|ba27b7dd-77db-7f6f-9b8c-2a3111d9d432]]
{{note|The example classes discussed in this section appear in the subdirectory ''datagram'' of the example directory. }}
Using datagram sockets implies a few differences from the preceding schemes. You create a [[ref:libraries/net/reference/network_datagram_socket_chart|NETWORK_DATAGRAM_SOCKET]] or a <eiffel>UNIX_DATAGRAM_SOCKET</eiffel> as before. You do not need to call <eiffel>listen</eiffel>, <eiffel>accept</eiffel> or <eiffel>connect</eiffel>. Once you have created your sockets using the <eiffel>make_client</eiffel> or <eiffel>make_server</eiffel> creation procedure, you can use the <eiffel>send</eiffel> and <eiffel>received</eiffel> routines, specifying the socket to which you want to send, or from which you want to receive.
The command <eiffel>send</eiffel> takes an argument of type [[ref:libraries/net/reference/datagram_packet_chart|DATAGRAM_PACKET]] , and the query <eiffel>received</eiffel> returns a result of this type. [[ref:libraries/net/reference/datagram_packet_chart|DATAGRAM_PACKET]] is an heir of class [[ref:libraries/net/reference/packet_chart|PACKET]] , itself an heir of [[ref:libraries/base/reference/array_chart|ARRAY]] [ [[ref:libraries/base/reference/character_8_chart|CHARACTER]] ]. Class [[ref:libraries/net/reference/datagram_packet_chart|DATAGRAM_PACKET]] makes it possible, through the query <eiffel>number</eiffel> and the associated command <eiffel>set_number</eiffel>, to associate a packet number with each packet. This compensates for lack of guaranteed sequencing of datagram sockets: the sender can number packets through <eiffel>set_number</eiffel>, and the receiver can check that all packets have been received through <eiffel>number</eiffel>, asking the sender to re-emit missing objects.
The text of this example is similar to what appears below (in a more general setting involving event-driven computation) for the next example.

View File

@@ -0,0 +1,33 @@
[[Property:title|What is EiffelNet]]
[[Property:weight|0]]
[[Property:uuid|bf6d5904-9df8-451a-2ef9-0eee1200b60f]]
===What is EiffelNet?===
The client-server model of computing is quickly gaining ground as a way of optimizing network resources and of building sophisticated and flexible transactional applications.
The object-oriented method of software development, as embodied in Eiffel, potentially provides ideal support for developing client-server applications. The EiffelNet library described in this report makes this potential a reality by addressing one the crucial needs of client-server development: how to exchange objects between software systems. Systems using EiffelNet can exchange object structures with other systems running on the same machine or on different machines; in the case of different machines, the hardware architectures need not be the same. Objects are transmitted using sockets.
Writing client-server applications, especially their communication aspects, is an arduous task; developers must master many tricky details of socket programming and other low-level mechanisms. EiffelNet hides this complexity by providing ready-made classes addressing the most common cases, so that developers only have to describe what is specific to their application, letting EiffelNet handle the whole process of communication and synchronization between clients and servers. This part of EiffelNet is known as the ''predefined level'' and for many uses this is the only one that you will need to learn about. For developments requiring a finer degree of control, all the major mechanisms of socket programming are still accessible in a convenient form through more specialized classes.
In offering these varying levels of abstraction and control, EiffelNet follows the principles set by other libraries of the Eiffel environment (see for example the discussion of EiffelLex in reference [ [[Bibliography|2]] ]). Some users require a mechanism that will be very easy to learn and will hide all the details in common cases; other users (or the same at later stages of their work) need to access the whole array of facilities available at the level of the underlying hardware or software. The various components of the library should address all these needs.
This manual describes how to use EiffelNet at its various levels of abstraction.
===Background and prerequisites===
EiffelNet is a component of the Eiffel development environment.
This manual assumes the following prerequisites:
* Familiarity with the basic Eiffel concepts: classes, inheritance, features, assertions, exception handling (see reference [ [[Bibliography|1]] ]).
* Experience of writing software with Eiffel.
* Basic knowledge of the EiffelBase libraries, as described in reference [ [[Bibliography|2]] ].
Familiarity with the basic concepts of client-server computing will also be helpful (see for example reference [ [[Bibliography|3]] ]; if you are new to this area, however, you will still be able to use EiffelNet, as this manual defines all the needed concepts.
===Organization of this manual===
[[Clients and servers]] discusses the notion of client and server. [[An overview of EiffelNet Mechanisms]] presents an overview of EiffelNet's facilities. The sections titled [[Introduction to the examples]] through [[A more complex example]] describe the facilities in detail through a set of increasingly ambitious examples (whose texts may all be found in the directory '''$ISE_EIFFEL/examples/net''' of the Eiffel distribution). The final section is a short [[Bibliography]].

View File

@@ -0,0 +1,10 @@
[[Property:title|EiffelNet]]
[[Property:weight|4]]
[[Property:uuid|80fbd984-da52-b573-6282-33f499d67ab5]]
==EiffelNet Library==
Type: Library<br/>
Platform: Any
A library providing classes that facilitate network communication through sockets.

View File

@@ -0,0 +1,33 @@
[[Property:title|Networking]]
[[Property:weight|-9]]
[[Property:uuid|4ddb235c-fea6-ee00-05af-6493e2c652a7]]
==Network communication solutions==
Many Eiffel applications take advantage of networking. The Eiffel solutions to support networking include the EiffelNet, ZeroMQ and http_client libraries.
===Choosing the right libraries===
The libraries serve different, and complementary, purposes:
*EiffelNet enables Eiffel systems to use many Internet concepts and protocols: sockets, network address, IPV4, IPV6 and others.
*ZeroMQ supports direct messaging.
*http_client is a web client used to send HTTP requests (GET, POST, ...) and process the associated responses.
===Using the libraries===
Here is how to take advantage of one or more of the above libraries in your Eiffel system:
* '''EiffelNet''':
: manipulation of sockets, network addresses, IPv4, IPv6. But also basic limited implementation for a few protocols. <code lang="shell">$ISE_LIBRARY/library/net/net.ecf</code>
* '''ZeroMQ''':
: wrapping for the [http://zeromq.org/ ZeroMQ] Distributed Messaging . [https://svn.eiffel.com/eiffelstudio/trunk/Src/library/zeromq]
* '''http_client''':
: simple web client to send http (GET, POST, ...) request and receive associated response. <code lang="shell">$ISE_LIBRARY/contrib/library/network/http_client/http_client.ecf</code>