[San-Diego-pm] A few questions of Perl
Tkil
tkil at scrye.com
Thu Dec 10 12:59:48 PST 2009
(Alex -- it's generally better to keep conversations on the list; that
way, you get the benefit of more eyes, and others can find the
information later. Thanks!)
> >>>>> "Alex" == Alex (Yu) Hu <foxele at gmail.com> writes:
Alex> Thanks Tkil. I am pretty much using a similar way to build my
Alex> socket interface. Regarding the two sockets, I want both socket
Alex> to be readable and writable. Here is my code, it is working but
Alex> sometimes gives me strange behavior:
I don't know if it got munged by your mailer, but the indentation was
pretty messed up. After cleaning up whitespace, I got:
Alex> | $swe_sock = new IO::Socket::INET->new( PeerAddr=>'192.168.0.2',
Alex> | PeerPort=>1850,
Alex> | Proto=>'tcp',
Alex> | Type=>SOCK_STREAM,
Alex> | Reuse=>1 )
Alex> | || die "Can't connect to 192.168.0.2:1850 : $!\n";
Alex> |
Alex> | $swe_sock->autoflush(1);
Alex> |
Alex> | $dsp_sock = new IO::Socket::INET->new( PeerAddr=>'192.168.0.2',
Alex> | PeerPort=>1852,
Alex> | Proto=>'tcp',
Alex> | Type=>SOCK_STREAM,
Alex> | Reuse=>1 )
Alex> | || die "Can't connect to 192.168.0.2:1852 : $!\n";
Alex> |
Alex> | $dsp_sock->autoflush(1);
Alex> |
Alex> | $sel = IO::Select->new;
Alex> | $sel->add($swe_sock);
Alex> | $sel->add($dsp_sock);
Alex> |
Alex> | while ( @ready = $sel->can_read )
Alex> | {
Alex> | foreach $fh (@ready)
Alex> | {
Alex> |
Alex> | #read your data
Alex> | my $count = sysread($fh, $bytes, 1024);
Alex> |
Alex> | $string .= $bytes;
Alex> |
Alex> | if ($fh == $dsp_sock)
Alex> | {
Alex> | print "Message from DSP_Sock.\n";
Alex> | }
Alex> |
Alex> | if ($fh == $swe_sock)
Alex> | {
Alex> | print "Message From SWE_Sock.\n";
Alex> | }
Alex> |
Alex> | # do something with $line
Alex> | #print the results on a third socket
Alex> | &msg_proc($string);
Alex> | $string="";
Alex> | }
Alex> | }
A substantial problem is that you're not using "strict" nor
"warnings". Those would have told you at least one problem, which is
that you can't use "==" on IO::Socket objects...
Huh. Turns out you can. That surprises me:
| $ perl -Mstrict -MIO::Socket::INET= -lwe \
| 'my $s = IO::Socket::INET->new( PeerAddr => "www.google.com:80" ) or die $!;
| print 0+$s;'
| 21700712
So much for that theory. Anyway, a few comments:
1. "new Class->new( ... )" is ... odd. It might actually work (and
apparently it does, since you're getting something sometimes), but
you really only need one of those "new"s. I personally prefer the
latter, so you end up with just "Class->new( ... )".
2. Most of the arguments you are giving to the IO::Socket::INET
constructor are the defaults, so you can omit them.
3. IO::Socket has had 'autoflush' turned on for years now; if you're
in control of your perl install, and if this script is just for
limited internal testing, you can probably omit those calls as
well.
4. I suspect that you cut out some of the processing, but you don't
really need a separate $string variable; you can operate directly
on the $bytes in the main loop.
Anyway, a cleaned-up version of the code. First, always use 'strict'
and 'warnings':
| #!/usr/bin/perl
|
| # paranoia
| use strict;
| use warnings;
Out of habit, I try to avoid bringing anything into my namespace that
I don't need. This is probably overkill, but better that you miss
something now (and have perl tell you that you need it), than to
unexpectedly get a value or function brought in.
| # limit imports
| use IO::Socket::INET qw();
| use IO::Select qw();
And I like to put all the "magic values" into one place up top:
| # constants
| my $SWE_ADDR = '192.168.0.2:1850';
| my $DSP_ADDR = '192.168.0.2:1852';
I almost made a subroutine for these two, but I managed to restrain
myself. (Did I mention something about how I dislike repetition?)
| # set up sockets
| my $swe_sock = IO::Socket::INET->new( PeerAddr => $SWE_ADDR,
| Reuse => 1 )
| or die "Can't connect to $SWE_ADDR: $!\n";
|
| my $dsp_sock = IO::Socket::INET->new( PeerAddr => $DSP_ADDR,
| Reuse => 1 )
| or die "Can't connect to $DSP_ADDR: $!\n";
|
| # listen to both sockets
| my $sel = IO::Select->new();
| $sel->add( $swe_sock );
| $sel->add( $dsp_sock );
|
| while ( my @ready = $sel->can_read() )
| {
| foreach my $fh ( @ready )
| {
| my $bytes;
| my $count = sysread( $fh, $bytes, 1024 );
|
| if ( $fh == $dsp_sock )
| {
| print "Message from DSP_Sock.\n";
| }
| elsif ( $fh == $swe_sock )
| {
| print "Message From SWE_Sock.\n";
| }
|
| &msg_proc( $bytes );
| }
| }
That compiled and ran... but as you said, it didn't do anything. Next
we try some debugging prints:
| while ( my @ready = $sel->can_read() )
| {
| print STDERR "select: " . @ready . " handles ready\n";
| foreach my $fh ( @ready )
| {
| my $bytes;
| my $count = read( $fh, $bytes, 1024 );
| print STDERR "read: $fh: $count bytes\n";
|
| if ( $fh == $dsp_sock )
| {
| print STDERR "Message from DSP_Sock.\n";
| }
| elsif ( $fh == $swe_sock )
| {
| print STDERR "Message From SWE_Sock.\n";
| }
|
| # &msg_proc( $bytes );
| }
| }
And running it gave me just this:
| $ ./alex-sockets-2.plx
| select: 1 handles ready
For comparison, adding debugging to the code I posted yesterday
evening gives me output something like this:
| $ ./alex-sockets.plx
| server: IO::Socket::INET=GLOB(0x19ef3f8)
| output: IO::Socket::INET=GLOB(0x1939388)
| select: IO::Select=ARRAY(0x19ef260)
| IO::Select=ARRAY(0x19ef260): 1 sockets ready
| new client: IO::Socket::INET=GLOB(0x19ef578)
| IO::Select=ARRAY(0x19ef260): 1 sockets ready
| IO::Socket::INET=GLOB(0x19ef578): 4 bytes 'bar\n'
| IO::Select=ARRAY(0x19ef260): 1 sockets ready
| IO::Socket::INET=GLOB(0x19ef578): goodbye
| IO::Select=ARRAY(0x19ef260): 1 sockets ready
| new client: IO::Socket::INET=GLOB(0x19ef500)
| IO::Select=ARRAY(0x19ef260): 1 sockets ready
| IO::Socket::INET=GLOB(0x19ef500): 5 bytes 'quit\n'
| $
So it looks like it's blocking on the read... Oh, which I screwed up.
You had "sysread" in there, and it works fine. (There's even a
warning in 'perlfunc' about mixing "select" with "read".) Now the
output looks like this:
| $ ./alex-sockets-2.plx
| select: 1 handles ready
| read: IO::Socket::INET=GLOB(0xdb9200): 4 bytes
| Message From SWE_Sock.
| select: 1 handles ready
| read: IO::Socket::INET=GLOB(0xdb9200): 4 bytes
| Message From SWE_Sock.
| select: 1 handles ready
| read: IO::Socket::INET=GLOB(0xdb9200): 4 bytes
| Message From SWE_Sock.
| select: 1 handles ready
| read: IO::Socket::INET=GLOB(0xdb9080): 6 bytes
| Message from DSP_Sock.
| select: 1 handles ready
| read: IO::Socket::INET=GLOB(0xdb9080): 5 bytes
| Message from DSP_Sock.
I also set it up so that it sends the message to the other socket, and
that works fine.
(Those of strong constitution might be amused by the development
environment I was using to test this:
http://scrye.com/~tkil/perl/emacs-nerd.png
http://scrye.com/~tkil/perl/emacs-nerd-2.png
That's what I get for still doing all my mailing lists and newsgroups
on a remote system -- my local emacs is much prettier. :)
Anyway, Here's the final code:
------------------------------------------------------------------------
#!/usr/bin/perl
# paranoia
use strict;
use warnings;
# limit imports
use IO::Socket::INET qw();
use IO::Select qw();
# constants
# my $SWE_ADDR = '192.168.0.2:1850';
# my $DSP_ADDR = '192.168.0.2:1852';
my $SWE_ADDR = 'localhost:1850';
my $DSP_ADDR = 'localhost:1852';
# set up sockets
my $swe_sock = IO::Socket::INET->new( PeerAddr => $SWE_ADDR,
Reuse => 1 )
or die "Can't connect to $SWE_ADDR: $!\n";
my $dsp_sock = IO::Socket::INET->new( PeerAddr => $DSP_ADDR,
Reuse => 1 )
or die "Can't connect to $DSP_ADDR: $!\n";
# listen to both sockets
my $sel = IO::Select->new();
$sel->add( $swe_sock );
$sel->add( $dsp_sock );
while ( my @ready = $sel->can_read() )
{
print STDERR "select: " . @ready . " handles ready\n";
foreach my $fh ( @ready )
{
my $bytes;
my $count = sysread $fh, $bytes, 1024;
print STDERR "read: $fh: $count bytes\n";
if ( $fh == $dsp_sock )
{
print STDERR "Message from DSP_Sock.\n";
syswrite $swe_sock, $bytes;
}
elsif ( $fh == $swe_sock )
{
print STDERR "Message From SWE_Sock.\n";
syswrite $dsp_sock, $bytes;
}
# &msg_proc( $bytes );
}
}
------------------------------------------------------------------------
More information about the San-Diego-pm
mailing list