Talk About Network

Google


Register and Login
Nick
Password
Register create new account Sign up is FREE and you can post replies, new topics, bookmark posts and more!
Recover lost password


Programming > Perl Beginners > Re: Perl Socket...
Latest [ Topics | Posts ] Archive Post A New Topic Post a Reply
<< Topic < Post Post 2 of 2 Topic 11002 of 11982
Post > Topic >>

Re: Perl Sockets Communications

by krahnj@[EMAIL PROTECTED] (John W. Krahn) Apr 28, 2008 at 09:40 PM

Richard Luckhurst wrote:
> Hi List

Hello,

> I have just been given the task of finding out why a perl sockets liste=
ner
> application we have is no longer working correctly. The application was=
 written
> back in 2002 and I am told worked fine until we were forced to install =
a new
> server early this year. I have no idea what version of perl the old ser=
ver ran
> but the new server is running 5.8.8.

perldoc perlhist

[ SNIP ]

                  5.6.0         2000-Mar-22

         Sarathy  5.6.1-TRIAL1  2000-Dec-18     The 5.6 maintenance track=
=2E
                  5.6.1-TRIAL2  2001-Jan-31
                  5.6.1-TRIAL3  2001-Mar-19
                  5.6.1-foolish 2001-Apr-01     The "fools-gold" release.=

                  5.6.1         2001-Apr-08
         Rafael   5.6.2-RC1     2003-Nov-08
                  5.6.2         2003-Nov-15     Fix new build issues


It looks like probably version 5.6 or at least 5.005?

The change that would probably concern you is the change to safe signals =

in Perl 5.8.  Read the "Deferred Signals (Safe Signals)" section of:

perldoc perlipc

In fact, you should probably read through all of perlipc.


> The idea of the listener is to start and sit and wait for a connection.=
 When the
> connection occurs a child listener should be created. The child shgould=
 take
> care of receiving data and sending it onto a posgres database. After an=
 idle
> timeout the child should exit.
>=20
> What is happening is when the child exits it is killing the parent proc=
ess as
> well. I am told that this did not happen on the previous server and the=
 parent
> process would stay running until the server needed a reboot or the proc=
ess was
> manually killed.
>=20
> I am not very familiar with perl so I am asking the list for any guidan=
ce as to
> where the script may be failing.
>=20
> I am including the code for the listener below.
>=20
> #!/usr/bin/perl

use warnings;
use strict;


> #
> # Name: listener.pl
> #
> #
> # Sub index:
> #   exCheckTrustedIP
> #   GetSock
> #   process_line
> #   ProcessRequests
> #   Reaper
> #   ServiceClients
> #   SetProperties
> #   StartDaemon
> #
> #
>=20
> #-USE------------------------------------------------------------------=
---------
> $debug =3D 1;
>=20
> use IO::Socket;
> use POSIX ":sys_wait_h";   # (for WNOHANG)

That should now be:

use POSIX 'WNOHANG';


> use Fcntl;
>=20
> #-CONSTANTS------------------------------------------------------------=
---------
>=20
> my ($SERVER_ADDR, $SERVER_****T, $LISTENER, $DATABASE_NAME, $DATABASE_PO=
RT,
>     $DATABASE_HOST, $USER_ID, $PASSWORD, $DATABASE_LOCATION, $MESSAGE_T=
IMEOUT,
>     $IDLE_TIMEOUT,) =3D "";

That is the same as:

my $SERVER_ADDR =3D "":
my ($SERVER_****T, $LISTENER, $DATABASE_NAME, $DATABASE_****T,
     $DATABASE_HOST, $USER_ID, $PASSWORD, $DATABASE_LOCATION,=20
$MESSAGE_TIMEOUT,
     $IDLE_TIMEOUT,);


> # Special cases are files and the associated database that they reside =
in.
> # It is basically saying if you see this table name, regardles of the d=
atabase you are=20
> # meant to be talking to (eg hack, dev), talk to the database specified=
=2E Was introduced so that
> # time records were always written to the "exodus' database.
> # This could be moved at a later date to be read from an actual file.
> # Note that the ip_access tables should also be in any database specifi=
ed in the
> # list.=20
> # Format table_name =3D> table_name,database_name,hostname,****tname,use=
r_id,password
> =20
> %SPECIAL_CASES =3D '';

Hashes should be assigned key/value pairs.  With warnings enabled that=20
would produce a warning message.


> %SPECIAL_CASES =3D

But why the other assignment?  You are just overwriting the ( '', undef=20
) pair that you previously assigned.


>                 (
>                 ex_time =3D> "ex_time:exodus:203.xxx.xxx.x:5432:xxxxxx:=
xxxxxxx",
>                 time_control =3D> "time_control:exodus:203.xxx.xxx.x:54=
32:xxxxxx:xxxxxxx"
>                 );
>=20
>=20
> sub StartDaemon
> {
>        =20
>         print "Listener started\n";
>        =20
>     SetProperties($_[0]);
>=20
>     my $pid;
>     if ($pid =3D fork()) {
>         waitpid($pid, 0);
>     } else {

If $pid is false it could be because either this is the child process or =

because fork() failed.  You are assuming that fork() was successful?


>         if ($pid =3D fork()) { exit; }

Why are you fork()ing again and exit()ing?


>         $0 =3D "$LISTENER: accepting connections on $SERVER_****T";  # f=
or =B4ps=B4
>         ServiceClients( GetSock() );       # wait for incoming requests=

>     }   =20
> }
> #----------------------------------------------------------------------=
---------
> # Changes:
> #   13AUG03 DT  Created sub.
>=20
> sub SetProperties
> {
>     my %properties =3D GetAllSiteRecordValues($_[0]);       # eg. $_[0]=
 =3D "hack"
>=20
>     $SERVER_ADDR   =3D $properties{"listener.server.addr"}; # eg. 203.x=
xx.xxx.x
>     $SERVER_****T   =3D $properties{"listener.server.****t"}; # eg. 15555=

>     $DATABASE_NAME =3D $properties{"listener.db.name"};     # eg. hack
>=20
>     $DATABASE_****T =3D $properties{"listener.db.****t"};     # eg. 5432
>     $DATABASE_HOST =3D $properties{"listener.db.addr"};     # eg. 203.x=
xx.xxx.x
>     $USER_ID       =3D $properties{"listener.db.user.id"};  # eg. beanm=
an
>     $PASSWORD      =3D $properties{"listener.db.user.password"};
>=20
>     $MESSAGE_TIMEOUT =3D $properties{"listener.timeout.message"}; # eg.=
 30  # Max pause WITHIN a MESSAGE (seconds)
>     $IDLE_TIMEOUT    =3D $properties{"listener.timeout.idle"};    # eg.=
 600 # Max Pause BETWEEN messages (seconds)
>=20
>     $LISTENER          =3D $DATABASE_NAME. "_listener";
>     $DATABASE_LOCATION =3D "dbname=3D$DATABASE_NAME;host=3D$DATABASE_HO=
ST;****t=3D$DATABASE_****T";
>=20
>     return 1;
> }
> #----------------------------------------------------------------------=
---------
>=20
> sub GetSock {
> #     unlink $sockname;
>     my $sock =3D IO::Socket::INET->new(
>                    Local****t  =3D> $SERVER_****T,
>                    Type       =3D> SOCK_STREAM,
>                    Reuse      =3D> 1,
>                    Listen     =3D> 10,
>                ) or die "$0: error starting $LISTENER daemon on '$SERVE=
R_****T': $@[EMAIL PROTECTED]
";
>     # you might want to change permissions and owner****p, e.g.:
>     #chmod 0600, $sockname;
>     #chown scalar getpwnam('nobody'), 0, $sockname;
>     return $sock;
> }
> #----------------------------------------------------------------------=
---------
>=20
> sub ServiceClients {
>     my $sock =3D ****ft;
>     $SIG{CHLD} =3D \&Reaper;
>=20
>     my $client;
>     while ( $client =3D $sock->accept() ) {
>         my $pid =3D fork();  die "Cannot fork\n" unless defined $pid;
>         if ($pid) {                   # parent
>             close $client;            # no use to parent
>             next;                     # be ready for another client
>         }
>         # child
>         close $sock;                  # no use to child
>         ProcessRequests($client);
>         exit;                         # terminate child
>     }
> }
> #----------------------------------------------------------------------=
---------
>=20
> sub ProcessRequests {
>     my $sock =3D ****ft;
>     my ($inline, $outline) =3D "";

That is the same as:

     my $inline =3D "";
     my $outline;


>     $| =3D 1;                 # don't buffer output - shouldn't need \n=
 to print

Note that $| only applies to the currently selected filehandle.  To=20
which filehandle did you want this to apply?


>     $0 =3D "$LISTENER: child handling requests...";  # for =B4ps=B4
>=20
>     my $db_handle =3D &ex_open_pg_connection($DATABASE_LOCATION, $USER_=
ID, $PASSWORD);
>     if (!$db_handle) {
>         exCloseWithError($sock, "Database error: Couldn't connect to da=
tabase");
>     }
>     exCheckTrustedIP($db_handle, $sock);
>=20
>     # Set non-blocking IO (from O'Reilly Perl)
>     $flags =3D '';
>     fcntl($sock, F_GETFL, $flags) or die "Error: $!\n";
>     $flags |=3D O_NONBLOCK;
>     fcntl($sock, F_SETFL, $flags) or die "Error: $!\n";
>=20
>     # Read 8192 bytes at a time until hex 04 then start again
>=20
>     $buf =3D "";
>     $prev_message_time  =3D time; # time last message was received.
>     $start_message_time =3D '';       # > null means partial message re=
ceived
>    =20
>         open (LOG, ">>/var/log/exodus/listener.log");

You should *always* verify that the file opened correctly:

         open LOG, '>>', '/var/log/exodus/listener.log' or die "Cannot=20
open '/var/log/exodus/listener.log' $!";


>     while (1) {
>         # Check how long its been since last message received
>         # If more than "IDLE_TIMEOUT" seconds then exit (terminate chil=
d process)
>         # Attempt to read data into buffer.
>         $i =3D recv $sock, $buf, 8192, '';
>         if (!$buf) {
>             # No data received - check for errors and timeouts.
>             if ($i < -1) {
>                 exCloseWithError($sock, "Internal error: TCP Error. Rea=
d() returned: $i");
>             }
>=20
>             # Check whether message receive in progress - if so check f=
or timeout.
>             $cur_time =3D time;
>             if ($start_message_time) {
>                 if ($cur_time > $start_message_time + $MESSAGE_TIMEOUT)=
 {
>                     exCloseWithError($sock, "Message timeout ($MESSAGE_=
TIMEOUT) seconds.  Please retry.");
>                 }
>             } else {
>                 # no message currently being received - check for how l=
ong it's been quiet...
>                 if ($cur_time > $prev_message_time + $IDLE_TIMEOUT) {
>                     exCloseWithError($sock, "A timeout has occurred aft=
er $IDLE_TIMEOUT seconds of inactivity. >> Connection closed.");
>                 }
>             }
>         }
>         else
>         {
>         # Some data received - check what we got.
>             if (($eom_idx =3D (index $buf, "\x04")) > -1)
>             {
>                 # End_Of_Message received.  Finalize bigbuf - should no=
w be entire message string
>                 $end_buf   =3D substr($buf, 0, $eom_idx);
>                 $bigbuf   .=3D $end_buf;    # should now be in form chk=
lenRMidRMdata
>=20
>                 $outline =3D exProcessMessage($db_handle, $sock, $bigbu=
f);
>                =20
>                 print LOG "OUT:$outline\n";
>                                =20
>                 printf $sock $outline;
>=20
>                 $prev_message_time =3D time;
>                 $bigbuf =3D substr($buf, $eom_idx+1);
>                 $buf =3D "";
>                 $start_message_time =3D '';       # NO message currentl=
y being received
>             } else {
>                 $start_message_time =3D time;
>                 $bigbuf .=3D $buf;
>                 $buf =3D "";
>             }
>         }
>     }
>    =20
>     close LOG;
>     &ex_close_pg_connection ($db_handle);
> }
> #----------------------------------------------------------------------=
---------
> # Checks if IP address at other end of socket is trusted, if not send e=
rror
> # message to socket and exit.
>=20
>=20
> sub exCheckTrustedIP
> {
>     my ($db_handle, $sock, $debug) =3D @[EMAIL PROTECTED]
>=20
>     # Get client IP address
>     my $other_end      =3D getpeername($sock)
>     or die "$LISTENER: Couldn't identify other end: $!\n";
>     my ($****t, $iaddr) =3D unpack_sockaddr_in($other_end);
>     my $ip_address     =3D inet_ntoa($iaddr);
>=20
>     # Confirm that client IP is trusted - if full IP not in table, test=
 on subnet
>     my $read_ptr  =3D ex_read_pg_data ($db_handle, "", "trusted_ips", "=
EQ", $ip_address, "", "0", "", "");
>     my $found_ip  =3D ****ft (@[EMAIL PROTECTED]
);
>=20
>     if ($found_ip)
>     {
>         send $sock, "OK: Successfully connected to $SERVER_ADDR:$SERVER=
_****T - Hello, $ip_address\n", '';
>     }
>     else
>     {
>         # Test subnet - IP up to last . then 0 eg 203.113.254.0
>         my $ip_last_dot =3D rindex ($ip_address, ".");
>         my $subnet =3D substr($ip_address, 0, $ip_last_dot+1)."0";

Or alternately:

         ( my $subnet =3D $ip_address ) =3D~ s/[^.]+\z/0/;


>         my $read_ptr  =3D ex_read_pg_data ($db_handle, "", "trusted_ips=
", "EQ", $subnet, "", "0", "", "");
>         my $found_sub  =3D ****ft (@[EMAIL PROTECTED]
);
>=20
>         if ($found_sub) { send $sock, "OK: Successfully connected to $S=
ERVER_ADDR:$SERVER_****T - Hello, $ip_address on subnet $subnet", ''; }
>         else            { printf $sock "ERROR: $ip_address is not a tru=
sted IP address. Please contact Exodus Systems Sup****t if you believe thi=
s IP should be on our Trusted IP list."; }
>     }
> }
> #----------------------------------------------------------------------=
---------
> # Sends error message to client, then closes socket and exits
>=20
> sub exCloseWithError
> {
>     my ($sock, $err_msg, $debug) =3D @[EMAIL PROTECTED]
>=20
>     if ($debug) { print "exCloseWithError:ERROR: $err_msg"; }
>     print $sock "ERROR: $err_msg";
>     close $sock;
>     exit(1);
> }
> #----------------------------------------------------------------------=
---------
> # Processes a complete XML message string. Performs several checks for =
message
> # validity, then sends message to Host and returns output to client bef=
ore
> # returning control to calling code
>=20
> sub exProcessMessage
> {
>     my ($db_handle, $sock, $message, $debug) =3D @[EMAIL PROTECTED]
>     my ($file) =3D '';
>    =20
>     # Check for first record mark
>     $rm1idx =3D index ($message, $RM);
>     if ($rm1idx =3D=3D -1) {
>          print $sock "No Record mark in string - require 2";
>          return;
>     }
>=20
>     $chk_len =3D substr($message, 0, $rm1idx);
>     $msg     =3D substr($message, $rm1idx+1);
>     $msg_len =3D length($msg);
>     if ($msg_len !=3D $chk_len)
>     {
>         print $sock "Checksum doesn't match - ensure message length sen=
t is correct: your length>$chk_len< and message length>$msg_len<";
>          return;
>     }
>=20
>     @[EMAIL PROTECTED]
 =3D split(/$RM/, $msg);
>     $action  =3D ****ft (@[EMAIL PROTECTED]
);
>     my $file =3D lc $records[0];
       ^^
You are declaring $file a second time in the same scope.  With warnings=20
enabled you would have received a warnings message about this.


>     if ($action eq "W")=20
>     {=20
>         if ($SPECIAL_CASES{$file}) {$outline =3D exWriteSpecialCases($s=
ock, $db_handle, \@[EMAIL PROTECTED]
 $debug);}
>         else                       {$outline =3D exWriteToPG($db_handle=
, \@[EMAIL PROTECTED]
 $debug); }
>     }
>     elsif ($action eq "M") { $outline =3D exProcessWebpayString(\@[EMAIL PROTECTED]
 $debug); }
>     else { $outline =3D "received action ->$action<-"; }
>=20
>     if ($debug) {  Debug($outline, "outline", "", "", "connection respo=
nse", "T", "Y"); }
>=20
>     return $outline; #."\n";  # need \n for <socket> read to work...
> }
> #----------------------------------------------------------------------=
---------
>=20
> sub Reaper {
>     while (waitpid(-1,WNOHANG) > 0) {}

That is usually written as:

     1 while waitpid( -1, WNOHANG ) > 0;


>     $SIG{CHLD} =3D \&Reaper;
> }
>=20
> #----------------------------------------------------------------------=
---------
> sub exWriteSpecialCases
> {
>    =20
>     my ($sock, $db_handle, $records_ptr, $debug) =3D @[EMAIL PROTECTED]
>     my @[EMAIL PROTECTED]
 =3D @[EMAIL PROTECTED]
 you are just going to make a copy of @[EMAIL PROTECTED]
 then why not just make=20
@[EMAIL PROTECTED]
 the last argument and pass through the whole list instead of a=20
reference?


>     my $file    =3D lc (****ft (@[EMAIL PROTECTED]
));
>     my $key     =3D ****ft (@[EMAIL PROTECTED]
);

     my ( $file, $key, @[EMAIL PROTECTED]
 ) =3D @[EMAIL PROTECTED]
     $file =3D lc $file;


>     my ($output) =3D "RESP:";
>    =20
> #   @[EMAIL PROTECTED]
 should only have 1 element left now after having the first=
 two=20
> #   ****fted off
> =20
>     my @[EMAIL PROTECTED]
    =3D split (/\xFE/, $records[0]);=20
>=20
>     @[EMAIL PROTECTED]
 =3D split (/:/, $SPECIAL_CASES{$file});
>     my $DATABASE_LOCATION =3D "dbname=3D$data_location[1];host=3D$data_=
location[2];****t=3D$data_location[3]";
>     my $USER_ID           =3D $data_location[4];
>     my $PASSWORD          =3D $data_location[5];
>    =20
>     my $db_handle =3D ex_open_pg_connection($DATABASE_LOCATION, $USER_I=
D, $PASSWORD);
       ^^
You are declaring $db_handle a second time in the same scope.  With=20
warnings enabled you would have received a warnings message about this.


>     exCheckTrustedIP($db_handle, $sock);
>=20
>    =20
>     $output .=3D &ex_write_pg_data($db_handle, "F", $file, $key, \@[EMAIL PROTECTED]
 $debug);
>    =20
> #    close (DEBUG);
>      ex_close_pg_connection ($db_handle);
>    =20
>     return $output;
> }
> #----------------------------------------------------------------------=
---------
>=20
> 1; #return true

Is this code being used as a module?  If not then you don't need that=20
last line.



John
--=20
Perl isn't a toolbox, but a small machine shop where you
can special-order certain sorts of tools at low cost and
in short order.                            -- Larry Wall
 




 2 Posts in Topic:
Perl Sockets Communications
rluckhurst@[EMAIL PROTECT  2008-04-29 11:13:03 
Re: Perl Sockets Communications
krahnj@[EMAIL PROTECTED]   2008-04-28 21:40:43 

Post A Reply:
  Go here to Signup

AddThis Feed Button


About - Advertising - Contact - Frequently Asked Questions - Privacy Policy - Terms of Use - Signup

Contact
tan12V112 Fri Oct 10 15:06:11 CDT 2008.