Previous Up Next

10.3  Attachment

10.3.1  Attachment Protocol


Figure 10.1: Summary of the attachment protocol

The attachment process is summarised in Figure 10.3. It is initiated from the ECLiPSe side, by either calling remote_connect/3 or the more flexible remote_connect_setup/3 and remote_connect_accept/6 pair. In fact, remote_connect/3 is implemented using remote_connect_setup/3 and remote_connect_accept/6 with default values for some of the arguments.

The attachment can be divided into several phases:

  1. Initialisation and handshaking: the control connection is established, and handshaking between the two sides are carried out – checking that the remote protocols on the two sides are compatible; checking the pass-term.
  2. Establishing the ERPC connections and exchange of information (remote language, ECLiPSe name for the peer).
  3. User-defined initialisations.

The remote protocol version is stored in the flag remote_protocol_version, accessible via get_flag/2 as an integer version number. This version number should only change when the protocol is modified. Checking of the version ensures that the same (or at least compatible) versions of the protocol are used, so that the two sides behaves correctly. The version information is sent from the remote side (which must have its own copy of the version information), and the ECLiPSe side checks that this is compatible with the protocol version it is using. In order to cope with remote connections which may not be using the remote protocol, the ECLiPSe side waits only for a fixed period of time for the remote side to send the version information before timing out.

Time-out on the ECLiPSe side can also occur for forming any of the connections between the two sides, from the control connection to the peer queue. This is specified by the user in remote_connect_accept/6. If time-out occurs during the attachment, then the attachment process is abandoned, and the predicate fails (any connected sockets will be closed).

The detailed sequence of events for the attachment for the remote side (with some description of the relevant ECLiPSe side actions) are:

  1. ECLiPSe side: a socket server for the control connection is created, using the address Host/Port which can be specified by the user. It then waits to accept a socket stream for the control connection from the remote side. The user can specify the amount of time to wait before this operation times-out, which would then terminate the attachment.
  2. Remote side: create a client socket stream for the control connection, with an address compatible with Host/Port. The socket stream should be in blocking mode, and perform no translation on the data sent.
  3. Remote side: sends the remote protocol version information on the control socket in EXDR format and flush it. This should be a remote_protocol/1 term.
  4. ECLiPSe side: reads the EXDR version term from the control socket, and compares with the version on the ECLiPSe side. If the two protocols are compatible, it sends the EXDR string yes back to the remote side; otherwise it sends the ECLiPSe remote protocol version to the remote side and disconnects from the remote side (raising unimplemented functionality error). The ECLiPSe side waits at most 100 seconds after the control connection is established for the remote side’s version: after this the ECLiPSe side disconnects the control connection and raises out of range error.
  5. It is expected that the future versions of the protocol will remain unchanged up to this point at least, to ensure the proper handshaking and checking of versions.
  6. Remote side: write the ‘pass-term’ in EXDR format on the newly created control connection and flush it. This provides a simple security check: ECLiPSe side will check if the ‘pass-term’ matches the ‘pass-term’ it was given when the remote connection was initiated – for remote_connect/3, the pass-term is the empty string, but the user can specify any pass-term if remote_connect_setup/3 and remote_connect_accept/6 are used. If the terms are not identical, then the ECLiPSe side will discontinue the attachment process.
  7. Remote side: read from the control connection the ECLiPSe name for the control connection. This is sent in EXDR format, and is used to identify this particular remote attachment – the peer name for the peer. This name is needed when calling ec_rpc goals that refer to the peer.
  8. Remote side: write the name of the programming language (e.g. tcl, java) of the remote process on the control connection. This should be in EXDR string format, and the connection flushed.
  9. ECLiPSe side: read the name of the programming language and store it (it can be accessed later via peer_get_property/3). The ECLiPSe side now wait to accept the socket stream (using the same socket server as the control connection) for the ec_rpc connection. This can also time-out.
  10. Remote side: create a client socket stream for the ec_rpc connection, using the same Port as for the control connection. This stream should be in blocking mode, and perform no translation on the data sent. The server socket on the ECLiPSe will be closed after accepting this client.
  11. Remote side: read the control connection name again on the remote side, on the newly established ec_rpc connection. This is also sent in EXDR format. This is designed to verify that the ec_rpc connection is indeed connected to the ECLiPSe side.
  12. Remote side: the remote side now has control. Any user-defined initialisations on the remote side can now be performed, to make the remote side ready for the interaction. The remote side has the control initially. Note that user-defined initialisations on the ECLiPSe side is also performed after sending the control name on the ec_rpc connection. After the initialisation, ECLiPSe side will suspend and listen on the control connection for the remote side to give control back to the ECLiPSe side.

At the end of this, the remote side should be ready for normal interaction with the ECLiPSe side.

The remote_connect/3 or remote_connect_accept/6 predicate waits for the control to be handed back by the remote side before exiting. Thus when the predicate succeeds, the remote side has been attached and properly initialised, with ECLiPSe side having control.

The protocol does not specify how the remote side should be informed of the Host/Port address for the initial socket connection. The Address can be fixed before hand (with the Address argument instantiated, or the information can be transmitted either manually or via files. In addition, the remote process can be started from within ECLiPSe using the exec/3 command, with the host and port supplied as arguments.

In accepting client socket connections from the remote side, the ECLiPSe side is informed of the host of the remote side. This should either be the client’s hostname, or ’localhost’. After accepting the control connection, subsequent connections (for ec_rpc and any peer queues) are checked to ensure that they are from the same client. If not, the attachment is terminated by ECLiPSe. Using ’localhost’ as the name on either side will restrict the two sides to be on the same machine (and they must both use localhost for Host in the address, rather than the actual hostname).

10.3.2  An example

The following is a simple example of making a remote attachment on the remote side. In this case, the remote side is also an ECLiPSe program. A remote attachment by a program written in another programming language will need to provide a similar function in that language:

% Example code for making a remote attachment on the remote side

:- local variable(ecsidehost).

% remote_attach/6 makes a remote attachment to a host ECLiPSe program.
% Args:
%    +Host: host name on ECLiPSe side
%    +Port: port on ECLiPSe side
%    +Pass: `pass-term' - used to verify the connection
%    +Init: goal to call to perform any application specific initialisation
%    -Control: the remote side (local) name of the control connection
%    -Ec_rpc:  the remote side name of the ec_rpc connection
%    -EcSideControl: the ECLiPSe side name for the control connection

remote_attach(Host, Port, Pass, Init, Control, Ec_rpc, EcSideControl) :-
     % control connection
     new_client_socket(Host, Port, Control),
     % send the protocol version information to the ECLiPSe side
     % we are the remote side, so must have our own version info.
     write_exdr(Control, remote_protocol(1)), flush(Control),
     % read response from ECLiPSe side to make sure it is compatible...
     read_exdr(Control, IsSameVersion),
     (IsSameVersion == "yes" ->
         true 
     ; 
         writeln("Incompatible versions of remote protocol. Failing...."),
         fail
     ),
     % send pass-term; if this does not match the pass-term on the ECLiPSe
     % side, it will terminate the attachment
     write_exdr(Control, Pass), flush(Control),
     % ECLiPSe side name for connection
     read_exdr(Control, EcSideControl),
     % send language name to ECLiPSe side
     write_exdr(Control, "eclipse"), flush(Control),
     % Ec_rpc connection
     new_client_socket(Host, Port, Ec_rpc),
     % read control name again on Ec_rpc to verify connection
     % in a more sophisticated implementation, this should time-out
     (read_exdr(Ec_rpc, EcSideControl) ->
        true 
     ; 
 % if not verified, terminate connection
        close(Control), close(Ec_rpc),
        fail
     ),
     % Remote side now has control, call Init to perform application
     % specific initialisations
     call(Init),
     % hand control over to ECLiPSe side...
     write_exdr(Control, resume), flush(Control).


new_client_socket(Host, Port, Socket) :-
     socket(internet, stream, Socket),
     connect(Socket, Host/Port).

%----------------------------------------------------
% the following code make use of remote_attach/6 to make an attachment,
% and then disconnect immediately when ECLiPSe side returns control

test(Host, Port) :-
     % make the remote attachment. For this simple example, there is no
     % application specific initialisations, and the default pass-term
     % of an empty string is used...
     remote_attach(Host, Port, "", true, Control, Ec_rpc, _),
     % control has been given to ECLiPSe side,
     % wait for ECLiPSe side to return control....
     read_exdr(Control, _),
     % immediately disconnect from ECLiPSe side
     write_exdr(Control, disconnect), flush(Control),
     % wait for ECLiPSe side to acknowledge disconnect...
     read_exdr(Control, disconnect_yield),
     % clean up
     close(Control), close(Ec_rpc).

In addition to the attachment, the example contains a very simple example of using the protocol by exchanging control messages with the ECLiPSe side. After attachment, it disconnects the remote attachment as soon as control is handed back to it. For more details on the messages, see next section.

To try out the example, start an ECLiPSe session and initiate a remote attachment. This process is the ECLiPSe side:

(ECLiPSe side)
[eclipse 1]: remote_connect(Host/Port, Control, _).
Socket created at address chicken.icparc.ic.ac.uk/32436

Now start another ECLiPSe session, compile the example program, and make the attachment to the ECLiPSe side:

(Remote side)
[eclipse 4]: test('chicken.icparc.ic.ac.uk', 32436).

On the ECLiPSe side, remote_connect/3 should now succeed:

(ECLiPSe side)
Host = 'chicken.icparc.ic.ac.uk'
Port = 32436
Control = peer3
yes.
[eclipse 2]: 

The remote side is suspended, awaiting the ECLiPSe side to return control. To return control to the remote side, the ECLiPSe side should call remote_yield/1 (see section 10.6 for a description of remote_yield/1):

(ECLiPSe side)
[eclipse 2]: remote_yield(peer3).
Abort
[eclipse 3]:

In this simple example, when control is returned to remote side, it immediately disconnects, thus the remote_yield/1 on the ECLiPSe side is aborted (as disconnect was initiated from the remote side), as per the disconnect protocol.

(Remote side)
[eclipse 4]: test('chicken.icparc.ic.ac.uk', 32436).

yes.
[eclipse 5]: 

Previous Up Next