Previous Up Next

21.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.

Previous Up Next