in reply to Re: Perl Sockets
in thread Perl Sockets

Hello, Could you take a look at this, it should be working. I have been told now that it is possible that the ip address/port that I am using may not be configured to receive SEND messages. Anyway, could you look at the code and make any suggestions. Thanks. here is my perl coded program:
#!/usr/bin/perl -w require 5.002; use strict; use IO::Socket; use IO::Select; use DBI; use DBD::Oracle; our ($dbh); our ($find_min_request_rec, $find_response_rec); our ($price_call_rec, $price_response_rec); our ($find_resp_pr1,$find_resp_pr2,$find_resp_pr3); our ($find_resp_pr4,$find_resp_pr5); our $read_set ; our $rh; our $ns; our $buf; our $find_resp; our $iroam_req_type; # Create the receiving socket my $s = new IO::Socket::INET ( LocalHost => '172.17.8.201', # IP Address Host to list +en on LocalPort => '18490', # Port Number Host to listen on Proto => 'tcp', Listen => 5, Reuse => 1, ); die "Could not create socket: $!\n" unless $s; $read_set = new IO::Select(); # create handle set for reading $read_set->add($s); # add the main socket to the set connect_to_oracle_db(); print "At Host/Port listening...\n"; while (1) { # Continous Listening on Port for a connection # get a set of readable handles(blocks until at least one handle is + ready) my ($rh_set) = IO::Select->select($read_set, undef, undef, 0); # take all readable handles in turn foreach $rh (@$rh_set) { print "Process each readable handle...\n"; print "Value of rh is $rh...\n"; print "Value of rh_set is @$rh_set..\n"; # if it is the main socket then we have an incoming # connection and we should accept() it and then add # the new socket to the $read_set if ($rh == $s) { print "Main socket/incoming connection add to readable set\n"; $ns = $rh->accept(); $read_set->add($ns); ##already added at beginning print "Value of rh_set is @$rh_set.after adding connection\n"; # otherwise it is an ordinary socket and we should read #and process the request }else{ print "Its an ordinary socket,so read and process the request +...\n"; print "Connected from: ", $rh->peerhost();#Display Peer Co +nnection print " Port: ", $rh->peerport(), "\n"; $buf = <$rh>; $iroam_req_type = substr($buf,0,8);#Extract the requestor type print "Requestor type extracted is: $iroam_req_type\n";#Displa +y Iroam req if($buf) { # return normal input and process $buf print "Iroam transaction recd:\n";# Iroam Data print "$buf\n"; #Display value of the Iroam data from Iroa +m if ($iroam_req_type =~ /FindMin/){ $find_min_request_rec = $buf; }elsif ($iroam_req_type =~ /PriceCall/){ $price_call_rec = $buf; } pass_transaction_to_iroam_package(); $rh->send($find_response_rec); print "rh send value is: $rh->send($find_response_rec) \n"; }else { # the client has closed the socket print "Iroam client has closed the socket.\n"; # remove the socket from the $read_set and close it $read_set->remove($rh); close($rh); } } } } sub connect_to_oracle_db{ print "Connect to the Oracle Database sub....\n"; $dbh = DBI->connect( 'DBI:Oracle:dev', 'perluser', 'oraperl', {AutoCommit => 0, RaiseError => 1} ) or die "Couldn't connect to database:".DBI->errstr; } sub pass_transaction_to_iroam_package{ print "pass transaction to iroam package sub....\n"; my $v_param_iroam_resp; if ($iroam_req_type =~ /FindMin/){ my $csr = $dbh->prepare(q{ begin :v_param_iroam_resp:= WIRELESS_IROAM_PKG.Lookup_ +Min_Request(:find_min_request_rec); end; }); $csr->bind_param(":find_min_request_rec", $find_min_request_rec +); $csr->bind_param_inout(":v_param_iroam_resp", \$v_param_iroam_r +esp,400); $csr->execute() or die "Couldn't exe pass transaction iroam pkg sub ".DBI->err +str; $find_response_rec = $v_param_iroam_resp; $find_resp_pr1 = substr($find_response_rec ,0,90); $find_resp_pr2 = substr($find_response_rec ,91,90); $find_resp_pr3 = substr($find_response_rec ,182,90); $find_resp_pr4 = substr($find_response_rec ,273,90); $find_resp_pr5 = substr($find_response_rec ,364,11); }elsif ($iroam_req_type =~ /PriceCall/){ my $csr = $dbh->prepare(q{ begin :v_param_iroam_resp:= WIRELESS_IROAM_PKG.Price +_Call(:price_call_rec); end; }); $csr->bind_param(":price_call_rec", $price_call_rec); $csr->bind_param_inout(":v_param_iroam_resp", \$v_param_iroam_res +p,400); $csr->execute() or die "Couldn't exe pass iroam pkg sub ".DBI->er +rstr; $price_response_rec = $v_param_iroam_resp; print "Value of v param iroam resp is: $v_param_iroam_resp\n"; print "Value of price response is: $price_response_rec \n"; } print "Processed transaction through the Iroam package\n"; }

Replies are listed 'Best First'.
Re^3: Perl Sockets
by zentara (Cardinal) on Jul 20, 2007 at 11:34 UTC
    Sorry, I'm not very good at finding flaws in code that I can't run, and I don't have Oracle. Maybe if you could post a chopped down example that has some generic filler code for the Oracle db stuff, I could run it.

    As far as the comment about SEND messages go, that is why I posted the forking client for you to try. If you read "perldoc IO::Socket", "send and recv" are mentioned. If you don't use a bi-directional client, you need to somehow switch your client socket into recv mode, after the send. This usually involves some sort of protocol you develop, to signal to both ends of the socket, who should be sending and who should be receiving. The forking bi-directional client is an easy solution for that problem, one fork sends and the other receives.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Hello, Thanks for all your help. I stripped out all the unnecessary code in Script #1. You should be able to execute the script now. I also sent another script, Script #2 that you can execute to send the Find min message to the Script #1. Try executing these and see if you can get Script #2 to send the message successfully. If it will not work, I will take your suggestion and add the forking to the script. The problem is that i am not sure where to actaully add it to Script #1. Could you guide me as where to add it? Again, Thanks so much Here are the scripts: Script #1: This is the script with all the oracle and all other unneccesary code striped out of it. I aded code that will emulate the send of the message bypassing the oracle.
      #!/usr/bin/perl -w require 5.002; use strict; use IO::Socket; use IO::Select; our ($find_min_request_rec, $find_response_rec, $price_call_rec); our $read_set ; our $rh; our $ns; our $buf; our $iroam_req_type; # Create the receiving socket my $s = new IO::Socket::INET ( LocalHost => '172.17.8.201', LocalPort => '18490', Proto => 'tcp', Listen => 5, Reuse => 1, ); die "Could not create socket: $!\n" unless $s; $read_set = new IO::Select(); # create handle set $read_set->add($s);# add main socket to the set print "At Host/Port listening. Waiting to receive transactions..\n"; while (1) { # Continous Listening on Port # get a set of readable handles my ($rh_set) = IO::Select->select($read_set, undef, undef, 0); # take all readable handles in turn foreach $rh (@$rh_set) { print "Process each readable handle...\n"; if ($rh == $s) { print "Main socket/incoming connection..\n"; $ns = $rh->accept(); $read_set->add($ns); # otherwise it is an ordinary socket and # we should read and process the request }else{ print "An ordinary socket..\n"; print "Connected from: ", $rh->peerhost(); print " Port: ", $rh->peerport(), "\n"; $buf = <$rh>; $iroam_req_type = substr($buf,0,8); if($buf) { # return normal input and process $buf if ($iroam_req_type =~ /FindMin/){ $find_min_request_rec = $buf; }elsif ($iroam_req_type =~ /PriceCall/){ $price_call_rec = $buf; } $find_response_rec = 'GCD 0003002296392 ENGLISH YNNNNN00 +0024160000'; $rh->send($find_response_rec); }else { # the client has closed the socket $read_set->remove($rh); close($rh); } } } }
      Script #2: You can use this script to acyually send the Findmin message to the port. Execute this one first to send the message to the port. This first one up above will be the one receiving the message.
      #!/usr/bin/perl use strict; use IO::Socket; our $iroam_req = 'FindMin TLK 91271558098436895219S 95TestRSU3 +33333333 888888885555520070718103045'; our($sock); main_process(); sub main_process{ print "main process sub....\n"; open_socket(); print $sock $iroam_req; ####close($sock); ###sleep 5; ###open_socket(); ###print $sock $iroam_req; ###close($sock); ###sleep 5; } sub open_socket{ $sock = new IO::Socket::INET ( PeerHost=> '172.17.8.201', PeerPort => '18490', Proto => 'tcp', ); die "Could not create socket: $!\n" unless $sock; }

        You need to use non-blocking reads and writes with select model servers. You're already using send, but you are using <handle> to read.

        If you change this line $buf = <$rh>;

        to $rh-recv( $buf, 100 ); your demo code will probably work.

        However, that is only the first of the problems you will encounter for your application. The demo will only work because you responding to the inbound requests immediately. Once you start forwarding the queries to the DB, you will need to separate the receiving of the request from the sending of the response, otherwise all your clients will need to wait until the previous transaction completes.

        You could issue the query immediately after the receive, and then go back to servicing the listener for a while until the database has the data available, and then send the response; but I'm unaware of any DBI method for polling a statement handle to determine if the data is available yet. POE does it using child processes and another pair of sockets or pipes.

        I can see an analogous threads solution. You just make your connection to the DB in a separate thread, queue the inbound queries to it as they arrive, and the send back the response as they become available. By picking the responses off a second return queue, using $Q->pending or $Q->dequeue_nb you avoid blocking the server thread, thus ensuring you stay responsive to new requests.

        The DB queries would be effectively serialised through the Q's, but then they would almost certainly be serialised at the DB end in anycase.

        Finally, as you mentioned above, you need to coordinate the requests and response. A simple mechanism is to use a hash to relate stringified socket handles to actual socket handles. Eg. (Untested)

        sub DBthread { my( $Qin, $Qout ) = @_; my $dbh = DBI->create( ... ); while( my( $tag, $request ) = split ' : ', $Qin->dequeue ) { my $sth = $dbh->prepare( $request ); my $response = $sth->execute; $Qout->enqueue( "$tag : $response ); } } my( $QtoDB, $QfromDB ) = map{ new Thread->Queue } 1 .. 2; my $threadDB = threads->create( \&DBthread, $QtoDB, $QfromDB ); ... my $listener = new IO::Socket( .. ); my $sel = new IO::Select; my $sel->add( $listener ); my %clients; while( 1 ) { ... for my $fh ( $sel->can_read ) { if( $fh == $listener ) { my $client = $listener->accept; $sel->add( $client ); } else { $fh->recv( $buf, 100 ); ... $QtoDB->enqueue( "$fh : select something from somewhere" ) +; $clients{ $fh } = $fh; } while( $QfromDB->pending ) { my( $tag, $response ) = split ' : ', $QfromDB->dequeue; my $clientFH = $clients{ $tag }; $clientFH->send( $response ); $clientFH->shutdown( 2 ); $sel->delete( $clientFH ); } } }

        That's pretty much all you would need to stay responsive to new requests, and respond as quickly as the DB can service them. Two threads. No starting or stopping of threads or processes. No extra pipes or sockets to multiplex.

        A more sophisticated design might seek to interleave the handling of requests and replies, but that's probably overkill.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        Well BrowserUk answered your question, but here is a modified set of your client/server to assist you in debugging. This set works, but you need to read more than the client sends (or a bug appears which kills the server when the client exits......try changing the 1024 to 100 in the programs and watch).

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum