NAME

Net::Socket::NonBlock - Perl extension for easy creation multi-socket single-thread application, especially non-forking TCP servers

Version 0.06

SYNOPSIS

  # TCP port forwarder with logging
  # Works on Win32!
  
  use strict;
  use Net::Socket::NonBlock qw(SafeStr);

  $| = 1;
  
  my $LocalPort   = shift
        or die "Usage: $0 <LocalPort> <RemoteHost:RemotePort>\n";
  my $RemoteHost  = shift
        or die "Usage: $0 <LocalPort> <RemoteHost:RemotePort>\n";
  
  my $SockNest = Net::Socket::NonBlock->new(SelectT  => 0.1,
                                            SilenceT => 0,
                                            debug    => $^W,
                                            BuffSize => 10240,
                                           )
        or die "Error creating sockets nest: $@\n";
  
  $SockNest->Listen(LocalPort => $LocalPort,
                    Proto     => 'tcp',
                    Accept    => \&NewConnection,
                    SilenceT  => 0,
                    #ClientsST => 10,
                    Listen    => 10,)
        or die "Could not listen on port '$LocalPort': $@\n";
  
  my %ConPool = ();

  while($SockNest->IO())
        {
        my $Pstr = '';
        my $ClnSock = undef;
        my $SrvSock = undef;
        while (($ClnSock, $SrvSock) = each(%ConPool))
                {
                my $ClientID = sprintf("%15.15s:%-5.5s", $SockNest->PeerAddr($ClnSock), $SockNest->PeerPort($ClnSock));
                my $Str = undef;
                while(($Str = $SockNest->Read($ClnSock)) && length($Str))
                        {
                        $Pstr .= "  $ClientID From CLIENT ".SafeStr($Str)."\n";
                        $SockNest->Puts($SrvSock, $Str);
                        };
                if (!defined($Str))
                        {
                        $Pstr .= "  $ClientID CLIENT closed\n"; 
                        $SockNest->Close($ClnSock);
                        $SockNest->Close($SrvSock);
                        delete($ConPool{$ClnSock});
                        next;
                        };
                while(($Str = $SockNest->Read($SrvSock)) && length($Str))
                        {
                        $Pstr .= "  $ClientID From SERVER ".SafeStr($Str)."\n";
                        $SockNest->Puts($ClnSock, $Str);
                        };
                if (!defined($Str))
                        {
                        $Pstr .= "  $ClientID SERVER closed\n"; 
                        $SockNest->Close($ClnSock);
                        $SockNest->Close($SrvSock);
                        delete($ConPool{$ClnSock});
                        next;
                        };
                };
        if (length($Pstr))
                { print localtime()."\n".$Pstr; };
        };              
  
  sub NewConnection
        {
        my ($ClnSock) = shift
                or return;

        $ConPool{$ClnSock} = $SockNest->Connect(PeerAddr => $RemoteHost, Proto => 'tcp',);
        if(!$ConPool{$ClnSock})
                {
                warn "Can not connect to '$RemoteHost': $@\n";
                $SockNest->Close($ClnSock);
                delete($ConPool{$ClnSock});
                return;
                };
        return 1;
        };

  sub SafeStr($)
        {
        my $Str = shift
                or return '!UNDEF!';
        $Str =~ s{ ([\x00-\x1f\xff\\]) } { sprintf("\\x%2.2X", ord($1)) }gsex;
        return $Str;
        };

DESCRIPTION

This module provides simple way to work with number of non-blocking sockets. It hides most of routine operations with IO::Socket::INET, IO::Select and provides you the asynchronous Input-Output functions.

Module was designed as a part of a multi-connection SMTP relay for WinNT platform.

The Net::Socket::NonBlock methods

new(%PARAMHASH);

The new method creates the SocketsNest object and returns a handle to it. This handle is then used to call the methods below.

The SocketsNest itself is the table contains socket handlers, InOut buffers, etc. SocketsNest also contain a IO::Select object which is common for all sockets in SocketsNest.

To create new socket you will have to use Listen or Connect methods (see below). Also, socket could be created automatically during TCP connection accept procedure inside of IO method.

The %PARAMHASH could contain the following keys:

SelectT

SelectT is the timeout for IO::Select->can_read and IO::Select->can_write function. See IO::Select for details. Default is 0.1 second.

SilenceT

If no data was transferred trough socket for SilenceT seconds the socket will be closed. Default is '0'. If SilenceT = 0 socket will nether been closed by timeout.

This value is the default for all sockets created by Listen or Connect method if another value will not be provided in Listen or Connect parameters. Also, you will be able to change this parameter for any socket in nest using Properties method (see below).

BuffSize

The size of buffer for IO::Socket::INET->recv function (see IO::Socket::INET). Default if POSIX::BUFSIZ (see POSIX).

This is default for all sockets which will be created and could be overwritten by Listen, Connect or Properties.

debug

If true, additional debug info will be printed during program execution.

NestProperties([%PARAMHASH]);

The NestProperties method returns the hash in list context or pointer to the hash in scalar context. Hash itself is containing nest properties which are:

Sockets

The number of sockets currently active on this nest.

SelectT
SilenceT
BuffSize
debug

See new() for detailed explanation.

The following parameters could be changed if new value will be provided in the %PARAMHASH:

SelectT
SilenceT
BuffSize
debug
IO([$Errors]);

The most important method :) This method performs actual socket input-output, accept incoming connection, close sockets, etc. You have to call it periodically, as frequently as possible.

$Errors could be a reference to the array. After the IO() call this array will conatin the messages for errors ocured during the call. Note: IO() cleans this array every time.

IO always returns 1.

SelectT([$Timeout]);

If $Timeout is not specified the SelectT method returns a current value of SelectT.

If $Timeout is specified the SelectT method set the SelectT to the provided value and returns a previous one.

The Net::Socket::NonBlock methods for manipulating sockets

Listen(%PARAMHASH);

The Listen method create new socket listening on LocalAddr:LocalPort.

The Listen take the same list of arguments as IO::Socket::INET->new() with some additions:

SilenceT

Silence timeout. See new() for details.

Accept

Contains the pointer to the external accept function provided by you.

When the new connection will be detected by listening TCP socket the new socket will be created by IO::Socket::INET->accept(). After that the external Accept function will be called with just one parameter: the ID for the new socket.

External Accept have to return true value otherwise new socket will be closed and connection will be rejected.

MaxClients

The maximum number of simultaneous incoming connections.

If current number of children of this listening socket is bigger than MaxClients new connections are not accepted.

'0' mean 'do not accept new connections'. The default is '9999999999' which is quite close to unlimited.

ClientsST

The silence timeout for children sockets. Default is the nest SilenceT.

Connect(%PARAMHASH);

The Connect() method create new socket connected to PeerAddr:PeerPort.

The Connect() take the same list of arguments as IO::Socket::INET->new() with same additions as Listen(). The Proto key is required.

Connect() method returns the SocketID, the symbolic name which have to be used for work with particular socket in nest using Gets(), Puts(), Recv() and Properties() methods. In case of problems Connect() returns an undef value. $@ will contain an error message.

Important note

Listen and Connect are synchronous. So if connection establishing take a long time - for eaxmple because of slow DNS resolving - your program will be frozen for a long time.

Gets($SocketID, [$MaxLength]);

For TCP sockets the Gets method returns a string received from corresponding socket. "String" means (.*\n).

If data is available for reading but "\n" is not presented in first $MaxLength bytes, the $MaxLength bytes will be returned.

For non-TCP sockets the Gets works with blocks of data read from socket by single IO::Socket::INET->recv call. It is necessary to provide correct PeerAddr and PeerPort. So, if "\n" found in the block and length of string is no more than $MaxLength, the string will be returned. If no "\n" found in the block and block length is no more than $MaxLength, the whole block will be returned. If string is too long or block is too big, $MaxLength bytes will be returned.

Default $MaxLength is socket BiffSize. $MaxLength for Gets have to be no more than 32766. It will be adjusted automaticaly otherwise.

If no data available for reading, Gets returns empty string.

If socket closed or $SocketID is invalid Gets returns an undef value. $@ will contain an error message.

In list context method returns an array of 3 elements: [0] - string as in scalar context [1] - PeerAddr [2] - PeerPort

Note: Gets is not reading data from the socket but takes it from special buffer filled by IO method with data read from socket during last call.

If you did not read all the data available in buffer new data will be appended to the end of buffer.

Recv($SocketID, [$MaxLength]);

For TCP sockets the Recv method returns all data available from corresponding socket if data length is no more than $MaxLength. Otherwise $MaxLength bytes returned.

For non-TCP sockets the Recv works with blocks of data read from socket by single IO::Socket::INET->recv call. It is necessary to provide correct PeerAddr and PeerPort. So, if block length is no more than $MaxLength, the whole block will be returned. If block is too big, $MaxLength bytes will be returned.

Default $MaxLength is socket BiffSize.

If no data available for reading, Recv returns empty string.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

In list context method returns an array of 3 elements: [0] - string as in scalar context [1] - PeerAddr [2] - PeerPort

Note: Recv is not reading data from the socket but takes it from special buffer filled by IO method.

Read($SocketID, [$MaxLength]);

This method is little bit eclectic but I found it useful.

If string "\n" is presented in the buffer this method will act as Gets method. Otherwise it will act as Recv.

$MaxLength for Read have to be no more than 32766. It will be adjusted automaticaly otherwise.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

Puts($SocketID, $Data, $PeerAddr, $PeerPort);

The Puts method puts data to the corresponding socket outgoing buffer.

$PeerAddr:$PeerPort pair is the destination which $Data must be sent. If not specified these fields will be taken from socket properties. $PeerAddr:$PeerPort will be ignored on TCP sockets.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message. Otherwise it returns 1.

Note: Puts is not writing data directly to the socket but puts it to the special buffer which will be flushed to socket by IO method during next call.

Send($SocketID, $Data);

Just a synonym for Puts.

PeerAddr($SocketID);

For TCP sockets the PeerAddr method returns the IP address which is socket connected to or empty string for listening sockets.

For non-TCP sockets the PeerAddr method returns the IP address which was used for sending last time or IP address which is corresponding to data read by last Gets or Recv call.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

PeerPort($SocketID);

For TCP sockets the PeerPort method returns the IP address which is socket connected to or empty string for listening sockets. undef

For non-TCP sockets the PeerPort method returns the port which was used for sending last time or port which is corresponding to data read by last Gets or Recv call.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

LocalAddr($SocketID);

The LocalAddr method returns the IP address for this end of the socket connection.

If socket closed or $SocketID is invalid LocalAddr returns undef.

LocalPort($SocketID);

The LocalPort method returns the IP address for this end of the socket connection.

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

Handle($SocketID);

The Handle method returns the handle to the IO::Socket::INET object associated with $SocketID or undef if $SocketID is invalid or socket closed.

Properties($SocketID, [%PARAMHASH]);

The Properties method returns the hash in list context or pointer to the hash in scalar context. Hash itself is containing socket properties which are:

Handle

The handle to the socket associated with $SocketID. Read-only.

Input

The length of data in buffer waiting to be read by Gets or Recv. Read-only.

Output

The length of data in buffer waiting for sending to the socket. Read-only.

BytesIn

The number of bytes which was received from socket. Read-only.

BytesOut

The number of bytes which was sent out to socket. Read-only.

CTime

The socket creation time as was returned by time(). Read-only.

ATime

The time when socket was sending or receiving data last time. Read-only.

PeerAddr

The value is the same as returned by PeerAddr method. Read-only.

PeerPort

The value is the same as returned by PeerPort method. Read-only.

LocalAddr

The value is the same as returned by LocalAddr method. Read-only.

LocalPort

The value is the same as returned by LocalPort method. Read-only.

SilenceT

The 'silence timeout'. After SilenceT seconds of inactivity the socket will be closed. Inactivity mean 'no data send or receive'. 0 mean 'infinity'.

ClientsST

Make sense for TCP listening sockets only. This is the 'silence timeout' for children (created by incoming connection accepting) sockets. See Listen for details.

Clients

Make sense for TCP listening sockets only. Contains the number of child sockets active at the moment. Read-only.

MaxClients

Make sense for TCP listening sockets only. The maximum number of child sockets. See Listen for details.

Accept

Make sense for TCP listening sockets only. The pointer to the external Accept function. See Listen for details.

Parent

For sockets created automaticaly by accepting incoming TCP connection this field contain the SocketID of parent (listening) socket. For other sockets Parent contains empty string. Read-only.

BuffSize

The size of buffer for IO::Socket::INET->recv function.

Error

The message for last error ocured on this socket during last IO call. Or just an empty string if no errors.

The following parameters could be changed if new value will be provided in the %PARAMHASH:

SilenceT
BuffSize
MaxClients
ClientsST
ATime
Accept

It is useless to set MaxClients or ClientsST or Accept for any sockets except TCP listening sockets

If socket is closed or $SocketID is invalid Recv returns an undef value. $@ will contain an error message.

Close($SocketID [, $Flush [, $Timeout]]);

Put the "close" request for the socket $SocketID. The actual removing will be done by IO method during next call.

$Flush is a boolean parameter which tells IO method to flush the output buffer before close the socket.

$Timeout is an amount of seconds after that the socket will be closed even it still have some data in the output buffer.

Remember: it is important to call Close for all socket which have to be removed even they become to be unavailable because of send() or recv() error or silence timeout.

EXPORT

None.

AUTHOR

Daniel Podolsky, <tpaba@cpan.org>

SEE ALSO

IO::Socket::INET, IO::Select.