Front page | perl.perl5.porters |
Postings from February 2004
[perl #27044] IO::Socket::INET nonblocking mode fails on Win32 - here is the fix
From:
perlbug-followup
Date:
February 24, 2004 20:29
Subject:
[perl #27044] IO::Socket::INET nonblocking mode fails on Win32 - here is the fix
Message ID:
rt-3.0.8-27044-79741.19.7659858633568@perl.org
# New Ticket Created by cnd@spamcop.net
# Please include the string: [perl #27044]
# in the subject line of all future correspondence about this issue.
# <URL: http://rt.perl.org:80/rt3/Ticket/Display.html?id=27044 >
This is a bug report for perl from cnd@spamcop.net,
generated with the help of perlbug 1.33 running under perl v5.6.1.
-----------------------------------------------------------------
[Please enter your report here]
Subject: IO::Socket::INET nonblocking mode fails on Win32 - here is the fix
Ignore the "Site configuration information" below. this bug is present in
all IO::Socket::INET distributions up to and including 5.8.3
As per the subject - "Blocking" doesn't work under Win32. Specifically,
this call fails:-
$sockets{$ip}=IO::Socket::INET->new(PeerAddr=>$ip, PeerPort=>$p,
Proto=>$Proto, Type=>$Type, ReuseAddr=>1, Blocking=>0)
(exactly *how* it fails differs between perl versions, but it always
fails)
In summary -
1. the Win32 "$sock->blocking($arg->{Blocking})" call succeeds,
despite the fact that it appears to come back with an error code
under Win32. So this line, although it actually works fine,
wrongly stops the show:-
defined $sock->blocking($arg->{Blocking})
or return _error($sock, $!, "$!");
2. You've got to do these 2 things immediately before calling
connect() otherwise it will block anyway (despite the above)
setsockopt($sock, SOL_SOCKET, SO_DONTLINGER, 1); # Dont block on close
and
my $temp = 1; ioctl($sock, 0x8004667E, \$temp); # Don't let it block us.
3. The Win32 "connect()" call succeeds, although once again it appears
to come back with an error code, so the show gets stopped wrongly
again, unless you add this line after it (to return the correctly
connected socket handle):
return $sock if(($sock) &&( $arg->{Blocking} == 0 )); # Win32 requires this line
If you want to email me - cnd@spamcop.net - the source code of the
most recent INET.pm you've got lying around, I'll fix and test it
under Win32 and Linux and send it back.
Below is a test script.
I fixed the "INET.pm" module - here is an MS-DOS "fc" output showing
what you need to alter to make non-blocking work. "inet583.pm" is
your original file. "INET582.PM" is my fixed version.
Comparing files inet583.pm and INET582.PM
***** inet583.pm
if (defined $arg->{Blocking}) {
defined $sock->blocking($arg->{Blocking})
or return _error($sock, $!, "$!");
}
***** INET582.PM
if (defined $arg->{Blocking}) {
# defined $sock->blocking($arg->{Blocking}) # <-- this line always "fails" on Win32
$sock->blocking(0) if($arg->{Blocking}==0); # Blocking=>1 is already the default anyhow.
}
*****
***** inet583.pm
undef $@;
if ($sock->connect(pack_sockaddr_in($rport, $raddr))) {
# ${*$sock}{'io_socket_timeout'} = $timeout;
return $sock;
}
***** INET582.PM
# these 13 lines contributed by Chris Drake:-
if(defined $arg->{Blocking}) {
if($arg->{Blocking}) {
$sock->blocking($arg->{Blocking})
} else {
if($^O =~/Win32/i) {
setsockopt($sock, SOL_SOCKET, SO_DONTLINGER, 1); # Dont block on close
} else {
$sock->blocking(0);
}
my $temp = 1; ioctl($sock, 0x8004667E, \$temp); # Don't let it block us.
}
}
*****
***** inet583.pm
return _error($sock, $!, $@ || "Timeout")
***** INET582.PM
undef $@;
if ($sock->connect(pack_sockaddr_in($rport, $raddr))) {
# ${*$sock}{'io_socket_timeout'} = $timeout;
return $sock;
}
return $sock if(($sock) &&( $arg->{Blocking} == 0 )); # Win32 requires this line
return _error($sock, $!, $@ || "Timeout")
*****
***** inet583.pm
***** INET582.PM
*****
Here is my test script. It will fail under Win32 until you make the above
corrections, and then it will work nicely.
#!/usr/bin/perl
# This file is "block.pl" by Chris Drake.
use strict;
use bytes;
use Time::HiRes qw(time);
use IO::Socket; # For the server
my %sockets;
# my $ip="203.2.192.76";
my $ip="65.54.190.230"; # This is a hotmail mail server - change to some other IP to experiment with blocking
my $p=25;
my $Proto='tcp';
my $Type=SOCK_STREAM;
my $Timeout=3;
# if ($sockets{$ip}=IO::Socket::INET->new(PeerAddr=>$ip, PeerPort=>$p, Proto=>$Proto, Type=>$Type, ReuseAddr=>1 ) ) {
if ($sockets{$ip}=IO::Socket::INET->new(PeerAddr=>$ip, PeerPort=>$p, Proto=>$Proto, Type=>$Type, ReuseAddr=>1, Blocking=>0) ) {
warn "yes";
} else {
warn "no";
}
warn "s='$sockets{$ip}' e=$!";
my($b)='';
vec($b,fileno($sockets{$ip}),1)=1;
my($r,$w,$e);
while(1) {
my $n=select($r=$b,$w=$b,$e=$b,$Timeout);
my $rp=unpack("B*",$r);
my $wp=unpack("B*",$w);
my $ep=unpack("B*",$e);
print "n=$n r=$rp w=$wp e=$ep\n";
if(vec($r,fileno($sockets{$ip}),1)==1) {
my $buff;
sysread($sockets{$ip},$buff,16384);
print "buff:-\n$buff\n";
close($sockets{$ip});
exit(0);
}
}
#######################################################################
=head2 FirstSocket
Connects to every supplied IP address, returning the first one that accepts us.
you cn use 'ip:port' in the list - if no :port, uses $PeerPort by default.
B<Input Paramaters>
$Proto # 'tcp' usually (anything else is untested)
$Type # SOCK_STREAM usually (anything else is untested)
$Timeout # max number of seconds to wait before giving up on them all
$PeerPort # default port (used if not specified in the list)
@ips # list of IP addresses to try. (see ValidEmail(,2));
B<Output> (3 element list of...)
open socket glob
ip address we used
first 16k of data read from the open socket
=cut
#######################################################################
sub FirstSocket {
my($Proto,$Type,$Timeout,$PeerPort,@ips)=@_;
undef %sockets; my($now)=time; my($some,$bits_server,$null_server,$r,$w,$e)=(0,'','');
# my(%switch); #=('debug' => 1);
my(%switch)=('debug' => 1);
foreach my $ipp (@ips) {
my($ip,$p)=split(':',$ipp,2); $p=$PeerPort unless($p);
#NB: Blocking=>0 fails unless the latest/fixed IO/Socket/INET.pm is loaded (): this must be inside their while(1) loop:-
# defined $sock->blocking($arg->{Blocking}) (see) http://search.cpan.org/src/NWCLARK/perl-5.8.3/ext/IO/lib/IO/Socket/INET.pm
$sockets{$ip}=IO::Socket::INET->new(PeerAddr=>$ip, PeerPort=>$p, Proto=>$Proto, Type=>$Type, ReuseAddr=>1, Blocking=>0);
&write_log("FirstSocket trying $ip") if($switch{'debug'});
$some++;
}
while(($some)&&((time-$now)<=$Timeout)) {
$bits_server='';
foreach my $ip (keys %sockets) {
vec($bits_server,fileno($sockets{$ip}),1)=1 if($sockets{$ip});
}
#if(select($r=$bits_server,$w=$bits_server,$e=$bits_server,$Timeout)) { # Wait for read to be allowed}
if(select($r=$bits_server,undef,$e=$bits_server,$Timeout)) { # Wait for read to be allowed
foreach my $ip (keys %sockets) {
if($sockets{$ip}) {
if(vec($e,fileno($sockets{$ip}),1)) { # Error
close($sockets{$ip});undef $sockets{$ip};$some--;
&write_log("FirstSocket connect error: $ip") if($switch{'debug'});
} elsif(vec($r,fileno($sockets{$ip}),1)) { # Reading is OK
my($buff); recv($sockets{$ip}, $buff, 16384, 0); # Read upto 16K
if(length($buff)==0) { # Error
close($sockets{$ip});undef $sockets{$ip};$some--;
&write_log("FirstSocket read error: $ip") if($switch{'debug'});
} else { # Great - got one!
foreach my $ip2 (keys %sockets) {
close($sockets{$ip2}) if(($sockets{$ip2})&&($ip ne $ip2));# Close all the others
}
&write_log("FirstSocket success: $ip") if($switch{'debug'});
return ($sockets{$ip},$ip,$buff);
}
}
}
}
&write_log("Waiting on $some ...") if($switch{'debug'});
}
}
&write_log("FirstSocket nothing worked in ". (time-$now) ." seconds") if($switch{'debug'});
return(undef,undef,undef);
}
# Print out debugging info
sub write_log {
my($message)=@_;
my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
my($date)=sprintf("%02d/%02d %02d:%02d.%02d",$mon+1,$mday,$hour,$min,$sec);
print "$date $$ emailcheck.pl $message\n";
}
# chomp() on unix doesn't eat "\r"...
sub chompnl {
chop $_[0] while((substr($_[0],-1) eq "\015")||(substr($_[0],-1) eq "\012"));
}
sub ServerName {
return "hotmail.com";
}
[Please do not change anything below this line]
-----------------------------------------------------------------
---
Flags:
category=core
severity=low
---
Site configuration information for perl v5.6.1:
Configured by ActiveState at Mon Jun 17 21:32:50 2002.
Summary of my perl5 (revision 5 version 6 subversion 1) configuration:
Platform:
osname=MSWin32, osvers=4.0, archname=MSWin32-x86-multi-thread
uname=''
config_args='undef'
hint=recommended, useposix=true, d_sigaction=undef
usethreads=undef use5005threads=undef useithreads=define usemultiplicity=define
useperlio=undef d_sfio=undef uselargefiles=undef usesocks=undef
use64bitint=undef use64bitall=undef uselongdouble=undef
Compiler:
cc='cl', ccflags ='-nologo -O1 -MD -DNDEBUG -DWIN32 -D_CONSOLE -DNO_STRICT -DHAVE_DES_FCRYPT -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DPERL_MSVCRT_READFIX',
optimize='-O1 -MD -DNDEBUG',
cppflags='-DWIN32'
ccversion='', gccversion='', gccosandvers=''
intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
d_longlong=undef, longlongsize=8, d_longdbl=define, longdblsize=10
ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=4
alignbytes=8, usemymalloc=n, prototype=define
Linker and Libraries:
ld='link', ldflags ='-nologo -nodefaultlib -release -libpath:"C:/Perl\lib\CORE" -machine:x86'
libpth="C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Lib\" "C:\Perl\lib\CORE"
libs= oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib msvcrt.lib
perllibs= oldnames.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib netapi32.lib uuid.lib wsock32.lib mpr.lib winmm.lib version.lib odbc32.lib odbccp32.lib msvcrt.lib
libc=msvcrt.lib, so=dll, useshrplib=yes, libperl=perl56.lib
Dynamic Linking:
dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib -release -libpath:"C:/Perl\lib\CORE" -machine:x86'
Locally applied patches:
ACTIVEPERL_LOCAL_PATCHES_ENTRY
---
@INC for perl v5.6.1:
C:/Perl/lib
C:/Perl/site/lib
.
---
Environment for perl v5.6.1:
HOME (unset)
LANG (unset)
LANGUAGE (unset)
LD_LIBRARY_PATH (unset)
LOGDIR (unset)
PATH=C:\Perl\bin\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\Program Files\ATI Technologies\ATI Control Panel;C:\Program Files\SecureCRT\;C:\DOS;C:\WINDOWS\ORANT\bin;C:\PROGRA~1\COMMON~1\MGISHA~1\Video;C:\PROGRA~1\COMMON~1\MGISHA~1\SHARED~1\Bin;C:\Program Files\Microsoft Visual Studio\Common\Tools\WinNT;C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin;C:\Program Files\Microsoft Visual Studio\Common\Tools;C:\Program Files\Microsoft Visual Studio\VC98\bin
PERLDB_OPTS=RemotePort=127.0.0.1:2000
PERL_BADLANG (unset)
SHELL (unset)
-
[perl #27044] IO::Socket::INET nonblocking mode fails on Win32 - here is the fix
by perlbug-followup