indigo1tx has asked for the wisdom of the Perl Monks concerning the following question:

Here's my problem. I have to make an interactive client that receives and process incoming that. The trick part -where I'm stuck with- is that each transaction is enclose like this STXxxxxxxxxETX... no new line character, ever. My prototype works fine with the new line character... but I can't figure out how to read and process each transaction as it arrives. Here is my code: The server
#!/usr/bin/perl
#telcel server

$ETX = chr(03);
$STX=chr(03);

use IO::Socket;
use Sys::Hostname;
my $sock = new IO::Socket::INET(
                   LocalHost => 'localhost',
                   LocalPort =>10019,
                   Proto     => 'tcp',
                   Listen    => SOMAXCONN,
                   Reuse     => 1);
$sock or die "no socket :$!";
STDOUT->autoflush(1);

$|=1;


my($new_sock, $buf);

# print "$buf\n";

while ($new_sock = $sock->accept()) {
    # got a client connection, so read
    # line by line until end-of-file
# print "BUF: $buf";






    while (defined($buf = <$new_sock>)) {
#    while (defined($buf = split(/$ETX/,<$new_sock>))) {




   foreach ($buf) {
#                       chop($buf);
                        $buf =~ s/98/99/gi;
                        print($new_sock "$buf"),
                        print "$buf\n";

   }
    }
    close $new_sock;
}

The client
#!/usr/bin/perl
# client2way.pl - a client that writes to
# and reads from a server


require "./cgiCommon";


use IO::Socket;

#my $host = shift || '10.252.1.41';
$host = shift || 'localhost';
#my $port = shift || 10018;
my $port = shift || 10019;

my $sock = new IO::Socket::INET(
                  PeerAddr => $host,
                  PeerPort => $port,
                  Proto    => 'tcp');
$sock or die "no socket :$!";
$sock->autoflush(1);


use DBI;

if ($sock){
print "Connected to $host\n\n";
}


$counter="0";

while ($counter < 30){
# send message to server
#print $sock "HELLO\n";
# print server response to STDOUT

&Dates;

&get_id;
$transaction_id= "AM" . sprintf("%06d",$id);

$string= chr(02) . "98" . $transaction_id . $fecha . $hora . chr(03) . "\n";
#$string= chr(02) . "98" . $transaction_id . $fecha . $hora . chr(03);

print $sock "$string" or die;

print "REQ: $string\n";

&save_echo ($string);

$response = scalar <$sock>;

print "RES: $response\n";

&save_echo ($response);



sleep(10);
$counter++;

Replies are listed 'Best First'.
Re: Socket IO now new line
by tachyon-II (Chaplain) on May 07, 2008 at 02:19 UTC

    Just use getc to get each input char as it arrives, buffer and process as required

    C:\>type io.pl use IO::Socket; $|++; my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 1234, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ) or die "no socket :$!"; while ($new_sock = $sock->accept()) { my ($buf, $char); while ( defined($char = $new_sock->getc) ) { print $char; $buf .= $char; do{process($buf); $buf = ''} if $buf =~ m/END$/; exit if $buf =~ m/QUIT/; } } sub process { print "\nProcessing: $_[0]\n" } C:\>io.pl testing using: telnet localhost 1234END Processing: testing using: telnet localhost 1234END send data as a streamEND Processing: send data as a streamEND buffer input yourself and process as requireedEND Processing: buffer input yourself and prosess as requireedEND Hope that helpsQUIT C:\>
Re: Socket IO NO new line
by kyle (Abbot) on May 07, 2008 at 02:20 UTC

    Have you tried setting the input record separator (see perlvar)?

    $sock->input_record_separator( 'ETX' );

      Much more elegant! Just a note that you can't set this on a per file handle basis so the calling syntax (that avoids warnings) is as shown below.

      use IO::Socket; $|++; IO::Handle->input_record_separator('END'); my $sock = new IO::Socket::INET( LocalHost => 'localhost', LocalPort => 1234, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ) or die "no socket :$!"; while ($new_sock = $sock->accept()) { while ( defined( my $line = $new_sock->getline) ) { print "Got: $line\n"; } }
      I have a feeling that ETX in this case stands for the ASCII character Control-C (ASCII code 3). Similarly, STX stands for ASCII code 2, hence the reference to chr(02) and chr(03) in the OP's code. Perhaps the OP can verify that this is the case. In any case, setting the input record separator for the socket should work, e.g.:
      $sock->input_record_separator(chr(3)); while (<$sock>) { ... # $_ contains one complete message }

      For some history on what codes like STX and ETX originally were used for, see The ASCII Control Characters.

      Update: Just noticed this suspect line in the server code:

      $STX=chr(03);
Re: Socket IO NO new line
by jasonk (Parson) on May 07, 2008 at 02:36 UTC

    You can use sysread to read any available bytes from the stream without worrying about whether there is a newline in there or not. Put whatever you read into a buffer in case it was only part of a transaction...

    #!/usr/bin/perl -w use strict; use warnings; use IO::Socket; use IO::Select; my $listener = IO::Socket::INET->new( LocalPort => 42424, Listen => 1, ) or die "failed to create socket: $!"; my $select = IO::Select->new( $listener ); my %buffers = (); while ( my @ready = $select->can_read ) { foreach my $fh ( @ready ) { if ( $fh == $listener ) { my $new = $listener->accept; $buffers{ $new } = ''; $select->add( $new ); } else { $fh->sysread( $buffers{ $fh }, 1024, length( $buffers{ $fh + } ) ); while ( $buffers{ $fh } =~ s/STX(.*?)ETX// ) { process( $1 ); } } } } sub process { print "GOT: $_[0]\n"; }

    www.jasonkohles.com
    We're not surrounded, we're in a target-rich environment!
Re: Socket IO NO new line
by locked_user sundialsvc4 (Abbot) on May 07, 2008 at 02:42 UTC

    I do not profess to have taken the time to “grok” your particular requirement completely, but the general flow of any such server is going to amount to this:

    1. The server, as a whole, is “waiting for input, then receiving and processing that input, then waiting for more input,” until the appropriate signal finally arrives that says that it should terminate.
    2. Each time the server has received more input, it should append that input to a buffer of some kind.
    3. Each time it does so, the server should check to see if the buffer contains “a complete input.” (In other words: “STX..something..ETX”) while it has this, it should remove the complete string from the buffer, process it, and check again to see if there is yet-another occurrence of “a complete input,” repeating this procedure until there are no more. It will therefore repeat this loop “zero or more” times following each successful read.

    Each time the server “reads something from the input pipe,” it has no way to know if “a complete input” has arrived or not. Likewise, it doesn't know how many complete-inputs have arrived. But it can accumulate those readings into a buffer, then chomp zero-or-more “complete inputs” from that buffer as it discovers them.