The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

IPC::LDT - implements a length based IPC protocol

SCRIPT DATA

This manual describes version 2.03.

DESCRIPTION

Interprocess communication often uses line (or record) oriented protocols. FTP, for example, usually is such a protocol: a client sends a command (e.g. "LS") which is completed by a carriage return. This carriage return is included in the command which is sent to the server process (FTP deamon) which could implement its reading in a way like this:

  while ($cmd=<CLIENT>)
   {
    chomp($cmd);
    performCommand($cmd);
   }

Well, such record oriented, blocked protocols are very useful and simply to implement, but sometimes there is a need to transfer more complex data which has no trailing carriage return, or data which may include more carriage returns inside the message which should not cause the reciepient to think the message is already complete while it is really not. Even if you choose to replace carriage returns by some obscure delimiters, the same could happen again until you switch to a protocol which does not flag the end of a message by special strings.

On the other hand, if there is no final carriage return (or whatever flag string) within a message, the end of the message has to be marked another way to avoid blocking by endless waiting for more message parts. A simple way to provide this is to precede a message by a prefix which includes the length of the remaining (real) message. A reciepient reads this prefix, decodes the length information and continues reading until the announced number of bytes came in.

IPC::LDT provides a class to build objects which transparently perform such "length driven transfer". A user sends and receives messages by simple method calls, while the LDT objects perform the complete translation into and from LDT messages (with prefix) and all the necessary low level IO handling to transfer stream messages on non blocked handles.

IPC::LDT objects can be configured to transfer simle string messages as well as complex data structures. Additionally, they allow to delay the transfer of certain messages in a user defined way.

SYNOPSIS

Load the module as usual:

  use IPC::LDT;

Make an LDT object for every handle that should be used in an LDT communication:

  my $asciiClient=new IPC::LDT(handle=>HANDLE);
  my $objectClient=new IPC::LDT(handle=>HANDLE, objectMode=>1);

Now you can send and receive data:

  $data=$asciiClient->receive;
  @objects=$objectClient->receive;

  $asciiClient=$client->send("This is", " a message.");
  $objectClient=$client->send("These are data:", [qw(a b c)]);

Exports

No symbol is exported by default.

You can explicitly import LDT_CLOSED, LDT_READ_INCOMPLETE, LDT_WRITE_INCOMPLETE, LDT_OK and LDT_INFO_LENGTH which are described in section CONSTANTS.

Global Variables

Settings

Traces

You may set the module variable $IPC::LDT::Trace before the module is loaded (that means in a BEGIN block before the "use" statement) to activate the built in trace code. If not prepared this way, all runtime trace settings (e.g. via the constructor parameter traceMode) will take no effect because the trace code will have been filtered out at compile time for reasons of performance. (This means that no trace message will appear.)

If $IPC::LDT::Trace is set before the module is loaded, te builtin trace code is active and can be deactivated or reactivated at runtime globally (for all objects of this class) by unsetting or resetting of this module variable. Alternatively, you may choose to control traces for certain objects by using the constructor parameter traceMode.

So, if you want to trace every object, set $IPC::LDT::Trace initially and load the module. If you want to trace only certain objects, additionally unset $IPC::LDT::Trace after the module is loaded and construct these certain objects with constructor flag traceMode.

Assertions

It is a good tradition to build self checks into a code. This makes code execution more secure and simplifies bug searching after a failure. On the other hand, self checks decrease code performance. That's why you can filter out the self checking code (which is built in and activated by default) by setting the module variable $IPC::LDT::noAssert before the module is loaded. The checks will be removed from the code before they reach the compiler.

Setting or unsetting this variable after the module was loaded takes no effect.

CONSTANTS

Error codes

LDT_CLOSED

a handle related to an LDT object was closed when reading or writing should be performed on it;

LDT_READ_INCOMPLETE

a message could not be (completely) read within the set number of trials;

LDT_WRITE_INCOMPLETE

a message could not be (completely) written within the set number of trials;

METHODS

new()

The constructor builds a new object for data transfers. All parameters except of the class name are passed named (this means, by a hash).

Parameters:

Class name

the first parameter as usual - passed implicitly by Perl:

  my $asciiClient=new IPC::LDT(...);

The method form of construtor calls is not supported.

handle

The handle to be used to perform the communication. It has to be opened already and will not be closed if the object will be destroyed.

 Example:
  handle => SERVER

A closed handle is not accepted.

You can use whatever type of handle meets your needs. Usually it is a socket or anything derived from a socket. For example, if you want to perform secure IPC, the handle could be made by Net::SSL. There is only one precondition: the handle has to provide a fileno() method. (You can enorce this even for Perls default handles by simply using FileHandle.)

objectMode

Pass a true value if you want to transfer data structures. If this setting is missed or a "false" value is passed, the object will transfer strings.

Data structures will be serialized via Storable for transfer. Because of this, such a communication is usually restricted to partners which could use Storable methods as well to reconstruct the data structures (which means that they are written in Perl).

String transfer objects, on the other hand, can be used to cimmunicate with any partner who speaks the LDT protocol. We use Java and C clients as well as Perl ones, for example.

 Example:
  objectMode => 1

The transfer mode may be changed while the object is alive by using the methods setObjectMode() and setAsciiMode().

startblockLength

sets the length of the initial info block which preceds every LDT message coding the length of the remaining message. This setting is done in bytes.

If no value is provided, the builtin default value LDT_INFO_LENGTH is used. (This value can be imported in your own code, see section "Exports" for details.) LDT_INFO_LENGTH is designed to meet usual needs.

 Example:
  startblockLength => 4
traceMode

Set this flag to a true value if you want to trace to actions of the module. If set, messages will be displayed on STDERR reporting what is going on.

Traces for <all> objects of this class can be activated (regardless of this constructor parameter) via $IPC::LDT::Trace. This is described more detailed in section "CONSTANTS".

 Example:
  traceMode => 1

Return value:

A successfull constructor call replies the new object. A failed call replies an undefined value.

Examples:

  my $asciiClient=new IPC::LDT(handle=>HANDLE);
  my $objectClient=new IPC::LDT(handle=>HANDLE, objectMode=>1);

setObjectMode()

Switches the LDT object to "object trasnfer mode" which means that is can send and receive Perl data structures now.

Runtime changes of the transfer mode have to be exactly synchronized with the partner the object is talking with. See the constructor (new()) description for details.

Parameters:

object

An LDT object made by new().

Example:

  $asciiClient->setObjectMode;

setAsciiMode()

Switches the LDT object to "ASCII trasnfer mode" which means that is sends and receives strings now.

Runtime changes of the transfer mode have to be exactly synchronized with the partner the object is talking with. See the constructor (new()) description for details.

Parameters:

object

An LDT object made by new().

Example:

  $objectClient->setAsciiMode;

delay()

Sometimes you do not want to send messages immediatly but buffer them for later delivery, e.g. to set up a certain send order. You can use delay() to install a filter which enforces the LDT object to delay the delivery of all matching messages until the next call of undelay().

The filter is implemented as a callback of send(). As long as it is set, send() calls it to check a message for sending or buffering it.

You can overwrite a set filter by a subsequent call of delay(). Messages already collected will remain collected.

To send delayed messages you have to call undelay().

If the object is detroyed while messages are still buffered, they will not be delivered but lost.

Parameters:

object

An LDT object made by new().

filter

A code reference. It should await a reference to an array which will contain the message (possibly in parts). It should reply a true or false value to flag if the passed message has to be delayed.

It is recommended to provide a fast function because it will be called everytime send() will be invoked.

Example:

  $ldt->delay(\&filter);

with filter() defined as

  sub filter
   {
    # get and check parameters
    my ($msg)=@_;
    confess "Missed message parameter" unless $msg;
    confess "Message parameter is no array reference"
      unless ref($msg) and ref($msg) eq 'ARRAY';

    # check something
    $msg->[0] eq 'delay me';
   }

See undelay() for a complete example.

undelay()

Sends all messages collected by a filter which was set by delay(). The filter is removed, so that every message will be sent by send() immediatly afterwards again.

In case of no buffered message and no set filter, a call of this message takes no effect.

Parameters:

object

An LDT object made by new().

Beispiel:

  $ldt->undelay;

Here comes a complete example to illustrate how delays can be used.

filter definition:

  sub filter
   {
    # check something
    $msg->[0] eq 'delay me';
   }

usage:

  # send messages
  $ldt->send('send me', 1);    # sent;
  $ldt->send('delay me', 2);   # sent;
  # activate filter
  $ldt->delay(\&filter);
  # send messages
  $ldt->send('send me', 3);    # sent;
  $ldt->send('delay me', 4);   # delayed;
  $ldt->send('send me', 5);    # sent;
  $ldt->send('delay me', 6);   # delayed;
  # send collected messages, uninstall filter
  $ldt->undelay;               # sends messages 4 and 6;
  # send messages
  $ldt->send('send me', 7);    # sent;
  $ldt->send('delay me', 8);   # sent;
  

send(<message>)

Sends the passed message via the related handle (which was passed to new()). The message, which could be passed as a list of parts, is sent as a (concatenated) string or as serialized Perl data depending on the settings made by the constructor flag objectMode and calls of setObjectMode() or setAsciiMode, respectively.

In case of an error, the method replies an undefined value and stores both an error code and an error message inside the object which could be accessed via the object variables "rc" and "msg". (See CONSTANTS for a list of error codes.)

An error will occur, for example, if the handle related to the LDT object was closed (possibly outside the module).

An error is detected as well if a previous call of send() or receive() already detected an error. This behaviour is implemented for reasons of security, however, if you want to try it again regardless of the objects history, you can reset the internal error state by reset().

For reasons of efficiency, sent messages may be splitted up into parts by the underlaying (operating or network) system. The reciepient will get the message part by part. On the other hand, the sender might only be able to send them part by part as well. That is why this send() method retries writing attempts to the associated handle until the complete message could be sent. Well, in fact it stops retries earlier if an inacceptable long period of time passed by without being successfull. If that happens, the method replies undef and provides an error code in the object variable "rc". The caller should be prepared to handle such cases. Usually further access to the associated handle is useless or even dangerous.

Parameters:

object

An LDT object made by new().

message (a list)

All list elements will be combined to the resulting message as done by print() or warn() (that means, without separating parts by additional whitespaces).

Examples:

  $asciiClient->send('Silence?', 'Maybe.')
  or die $asciiClient->{'msg'};

  $objectClient->send({oops=>1, beep=>[qw(7)]}, $scalar, \@array);

Note: If the connection is closed while the message is sent, the signal SIGPIPE might arrive and terminate the complete program. To avoid this, SIGPIPE is ignored while this method is running.

The handle associated with the LDT object is made non blocking during data transmission. The original mode is restored before the method returns.

reset

If an error occurs while data are transmitted, further usage of the associated handle is usually critical. That is why send() and receive() stop operation after a transmission error, even if you repeat their calls. This should protect your program and make it more stable (e.g. writing to a closed handle migth cause a fatal error and even terminate your program).

Nevertheless, if you really want to retry after an error, here is the reset() method which resets the internal error flags - unless the associated handle was not already closed.

Parameters:

object

An LDT object made by new().

Example:

  $ldtObject->reset;

receive()

reads a message from the associated handle and replies it.

In case of an error, the method replies an undefined value and provides both a return code (see CONSTANTS) and a complete message in the object variables "rc" and "msg", respectively, where you can read them.

An error will occur, for example, if the handle related to the LDT object was closed (possibly outside the module).

An error is detected as well if a previous call of send() or receive() already detected an error. This behaviour is implemented for reasons of security, however, if you want to try it again regardless of the objects history, you can reset the internal error state by reset().

For reasons of efficiency, sent messages may be splitted up into parts by the underlaying (operating or network) system. The reciepient will get the message part by part. That is why this receive() method retries reading attempts to the associated handle until the complete message could be read. Well, in fact it stops retries earlier if an inacceptable long period of time passed by without being successfull. If that happens, the method replies undef and provides an error code in the object variable "rc". The caller should be prepared to handle such cases. Usually further access to the associated handle is useless or even dangerous.

Parameters:

object

An LDT object made by new().

The received message is replied as a string in ASCII mode, and as a list in object mode.

Example:

  $msg=$asciiClient->receive or die $asciiClient->{'msg'};

  @objects=$objectClient->receive or die $objectClient->{'msg'};

Note: If the connection is closed while the message is read, the signal SIGPIPE might arrive and terminate the complete program. To avoid this, SIGPIPE is ignored while this method is running.

The handle associated with the LDT object is made non blocking during data transmission. The original mode is restored before the method returns.

version()

replies the modules version. It simply replies $IPC::LDT::VERSION and is implemented only to provide compatibility to other object modules.

Example:

  # get version
  warn "[Info] IPC is performed by IPC::LDT ", IPC::LDT::version, ".\n";

ENVIRONMENT

FILES

SEE ALSO

NOTES

EXAMPLE

To share data between processes, you could embed a socket into an LDT object.

  my $ipc=new IO::Socket(...);
  my $ldt=new IPC::LDT(handle=>$ipc, objectMode=>1);

Now you are able to send data:

  my $dataRef=[{o=>1, lal=>2, a=>3}, [[qw(4 5 6)], [{oo=>'ps'}, 7, 8, 9]]];
  $ldt->send($dataRef) or die $ldt->{'msg'};

or receive them:

  @data=$ldt->receive or die $ldt->{'msg'};

AUTHOR

Jochen Stenzel (perl@jochen-stenzel.de)

COPYRIGHT

Copyright (c) 1998-2000 Jochen Stenzel. All rights reserved.

This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License distributed with Perl version 5.003 or (at your option) any later version. Please refer to the Artistic License that came with your Perl distribution for more details.