Datagram Connection (internet domain)

## 22.3 Datagram Connection (internet domain)

This type of communication is the most general one offered by ECLiPSe. It is based on packets sent from one process to another, perhaps across a network. Any machine which is reachable over the network can participate in the communication.

The communication protocol does not guarantee that the message will always be delivered, but normally it will be. Every packet represents a message which is read separately at the system level, however at the Prolog level the packet boundaries are not visible.1 The difference to stream communication is that there is no obligatory connection between the server and the client. First the socket has to be created, and then the process which wants to read from the it binds the socket to a name. Any other process can then connect directly to this socket using the connect/2 predicate and send data there. This connection can be temporary, and after writing the message to the socket the process can connect it to another socket, or just disconnect it by calling connect(Socket, 0).

Such datagram connection works only in one direction, namely from the process that called connect/2 to the process that called bind/2, however the connection in the other direction can be established in the same way.

Since ECLiPSe does not provide a link to the system call sendto(), the address where the packet should be sent to can be specified only using connect/2. If the next packet is to be sent to a different address, a new connect/2 call can be used. The socket can be disconnected by calling connect(s, 0/0).

The functionality of recvfrom() is not available, i.e., the sender has to identify itself explicitly in the message if it wants the receiver to know who the sender was.

Below is an example of a program that starts ECLiPSe on all available machines which are not highly loaded and accepts a hello message from them. Note the use of rsh to invoke the process on the remote machine and pass it the host name and port address. Note that this example is Unix specific.

>     % Invoke ECLiPSe on all available machines and accept a hello message
>     % from them.
>     connect_machines :-
>         machine_list(List),        % make a list of machines from ruptime
>         socket(internet, datagram, sigio(s)), % signal when data comes
>         bind(s, Address),
>         set_interrupt_handler(io, io_handler/0),
>         connect_machines(List, Address).

>     % As soon as a message arrives to the socket, the io signal will
>     % be sent and the handler reads the message.
>     io_handler :-
>         set_flag(enable_interrupts, off),
>         read_string(s, end_of_line, "", _, Message),
>         writeln(Message),
>         set_flag(enable_interrupts, on).

>     % Invoke eclipse on all machines with small load and let them execute
>     % the start/0 predicate
>     connect_machines([info(RHost, UpTime, Users, L1, _, _)|Rest],
>                       Host/Port
>                     ) :-
>         UpTime > 0,        % it is not down
>         L1 < 0.5,          % load not too high
>         Users < 3,         % not too many users
>         !,
>         concat_string(, Command),
>         exec(['rsh', RHost, 'eclipse', Host, Port, '-b',
>            '/home/lp/micha/sepia4/up.pl', '-e', 'start'], [], _),
>         connect_machines(Rest, Host/Port).
>     connect_machines([_|Rest], Address) :-
>         connect_machines(Rest, Address).
>     connect_machines([], _).

>     % ECLiPSe on remote hosts is invoked with
>     %          eclipse host port -b file.pl -e start
>     % It connects to the socket of the main process,
>     % sends it a hello message and exits.
>     start :-
>         is_built_in(socket/3),    % to ignore non-BSD machines
>         argv(1, SHost),
>         argv(2, SPort),
>         atom_string(Host, SHost),
>         number_string(Port, SPort),
>         get_flag(hostname, LHost),
>         socket(internet, datagram, s),   % create the socket
>         connect(s, Host/Port),           % connect to the main process
>         printf(s, "hello from %s\n%b", LHost).

>     % Invoke ruptime(1) and parse its output to a list of accessible
>     % machines in the form
>     %    info(Host, UpTime, Users, Load1, Load2, Load3).
>     machine_list(List) :-
>         % exec/2 cannot be used as it could overflow
>         % the pipe and then block
>         exec(['ruptime', '-l'], [null, S], P),
>         parse_ruptime(S, List),
>         close(S),
>         wait(P, _),
>         !.

>     % Parse the output of ruptime
>     parse_ruptime(S, [Info|List]) :-
>         parse_uptime_record(S, Info),
>         !,
>         parse_ruptime(S, List).
>     parse_ruptime(_, []).

>     % parse one line of the ruptime output
>     parse_uptime_record(S, info(Host, Time, Users, Load1, Load2, Load3)) :-
>         read_token(S, Host, _),
>         Host \== end_of_file,
>         read_token(S, Up, _),
>         (Up == up ->
>             read_time(S, Time),
>             read_token(S, ',', _),
>             read_token(S, Users, _),
>             read_token(S, _, _),
>             read_token(S, ',', _),
>             read_token(S, load, _),
>             read_token(S, Load1, _),
>             read_token(S, ',', _),
>             read_token(S, Load2, _),
>             read_token(S, ',', _),
>             read_token(S, Load3, _)
>         ;
>             read_time(S, _),
>             Time = 0
>         ).

>     % Parse the up/down time and if the machine is down, return 0
>     read_time(S, Time) :-
>         read_token(S, T1, _),
>         (read_token(S, +, _) ->
>             Days = T1,
>             read_token(S, Hours, _),
>             read_token(S, :, _)
>         ;
>             Days = 0,
>             Hours = T1
>         ),
>         read_token(S, Mins, _),
>         Time is ((24 * Days) + Hours) * 60 + Mins.

and here is a script of the session:

>     [eclipse 1]: [up].
>     up.pl      compiled traceable 4772 bytes in 0.08 seconds

>     yes.
>     [eclipse 2]: connect_machines.
>     sending to mimas3
>     sending to mimas8
>     sending to acrab23
>     sending to europa1
>     sending to europa5
>     sending to regulus2
>     sending to miranda5
>     sending to mimas2
>     sending to triton6
>     sending to europa2
>     sending to acrab7
>     sending to europa3
>     sending to sirius
>     sending to miranda6
>     sending to charon6
>     sending to acrab13
>     sending to triton1
>     sending to acrab20
>     sending to triton4
>     sending to charon2
>     sending to triton5
>     sending to acrab24
>     sending to acrab21
>     sending to scorpio
>     sending to acrab14
>     sending to janus5

>     yes.
>     [eclipse 3]: hello from mimas3
>     eclipse: Command not found.     % eclipse not installed here
>     hello from regulus2
>     hello from mimas8
>     hello from acrab20
>     hello from europa1
>     hello from mimas2
>     hello from miranda6
>     hello from miranda5
>     hello from europa3
>     hello from charon6
>     hello from charon2
>     hello from acrab24
>     hello from triton5
>     hello from acrab21
>     hello from janus5
>     hello from triton4
>     hello from triton6
>     hello from europa2
>     hello from europa5
>     hello from acrab23
>     hello from triton1
>     hello from acrab14
>     hello from acrab13
>     hello from acrab7

1
  The packet boundaries are not of much interest in Prolog because every Prolog term represents itself a message with clear boundaries.