Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > IO::Socket::INET on OSX or TCP stack problem

Reply
Thread Tools

IO::Socket::INET on OSX or TCP stack problem

 
 
Stuart Gall
Guest
Posts: n/a
 
      05-07-2009
Hello,
I have written a script using IO::Socket::INET to read and write data to
modbus devices. Using blocking IO ->read and ->write.
This is running on OSX V 10.4.11
I have the latest IO::Socket Library from CPAN.

Using perl 5.8.6 from OSX and I built perl 5.10 which has the same issue
The script will work for a few loops, maybe doing 100 or so modbus
read/writes
Then the sockets are closed for no apparent reason.

I copied the same script to another OSX machine running 10.4.11 and I
have the exact same problem.

I copied the script to a machine running Mandrivia 2009 and it works
just fine!

I did a tcpdump, what seams to be happening is that when an error occurs
in a TCP packet, the OSX end gets stuck in a loop requesting and re
requesting the packet.
Until eventually the other end sends RST and force-ably closes the socket.

Any Ideas what might be going on ?


TCPDUMP (192.168.251.3 is the OSX machine)

10:17:19.508241 IP 192.168.251.3.52312 > 192.168.251.102.502: P
160:172(12) ack 90 win 65535 <nop,nop,timestamp 1360281049 65303>
10:17:19.514522 IP 192.168.251.102.502 > 192.168.251.3.52312: P
90:101(11) ack 172 win 5669 <timestamp 65461 1360281049,nop,nop>
10:17:19.514544 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281049 65461>
That was a request and reply acknowledged - Normal
10:17:19.747871 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281049 65461>
Request
10:17:19.752164 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 163 1360281049,nop,nop>
Reply
10:17:19.752183 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281050 65461>
Rejected
10:17:20.501290 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 912 1360281050,nop,nop>
Resent
10:17:20.501355 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281051 65461>
Rejected
10:17:20.750835 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281051 65461>
Resend request (even though it was acknowledged) Is that legal ??
10:17:20.762936 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 1174 1360281051,nop,nop>
Accept
10:17:21.929985 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 2341 1360281051,nop,nop>
Reply Again
10:17:21.930046 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281054 65461>
Rejected
10:17:21.931308 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 2342 1360281054,nop,nop>
Resent
10:17:21.931324 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281054 65461>
Rejected
10:17:21.932624 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 2343 1360281054,nop,nop>
?

And so on
10:17:22.751058 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281055 65461>
10:17:22.759845 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 3171 1360281055,nop,nop>
10:17:24.778286 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 5189 1360281055,nop,nop>
10:17:24.778318 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281060 65461>
10:17:24.779660 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 5190 1360281060,nop,nop>
10:17:26.751479 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281063 65461>
10:17:26.758087 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 7169 1360281063,nop,nop>
10:17:30.470519 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 10881 1360281063,nop,nop>
10:17:30.470583 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281071 65461>
10:17:30.471828 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 10882 1360281071,nop,nop>
10:17:34.752192 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281079 65461>
10:17:34.754071 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 15164 1360281079,nop,nop>
10:17:37.576828 IP 192.168.251.102.502 > 192.168.251.3.52312: P
101:113(12) ack 187 win 5654 <timestamp 17987 1360281079,nop,nop>
10:17:37.576875 IP 192.168.251.3.52312 > 192.168.251.102.502: . ack 101
win 65535 <nop,nop,timestamp 1360281085 65461>
10:17:37.578139 IP 192.168.251.102.502 > 192.168.251.3.52312: . ack 187
win 5654 <timestamp 17988 1360281085,nop,nop>
10:17:50.754009 IP 192.168.251.3.52312 > 192.168.251.102.502: P
172:187(15) ack 101 win 65535 <nop,nop,timestamp 1360281111 65461>
10:17:50.761068 IP 192.168.251.102.502 > 192.168.251.3.52312: R
249188453:249188453(0) win 0
Now I think 192.168.251.102 gets fed up and boots us off

TIA
Stuart.
 
Reply With Quote
 
 
 
 
derykus@gmail.com
Guest
Posts: n/a
 
      05-07-2009
On May 7, 12:26*pm, Stuart Gall <(E-Mail Removed)> wrote:
> Hello,
> I have written a script using IO::Socket::INET to read and write data to
> modbus devices. Using blocking IO ->read and ->write.
> This is running on OSX V 10.4.11
> I have the latest IO::Socket Library from CPAN.
> ....


No ideas about the problem but I'd recommend showing
some of your code. Seeing actual code often jogs
memories in ways other problem descriptions can't.
What options did you pass to the IO::Socket::INET constructor,
Reuseaddr, Timeout, Blocking, etc.?

--
Charles DeRykus
 
Reply With Quote
 
 
 
 
Stuart Gall
Guest
Posts: n/a
 
      05-08-2009
On 2009-05-07 23:03:16 +0300, http://www.velocityreviews.com/forums/(E-Mail Removed) said:

> On May 7, 12:26*pm, Stuart Gall <(E-Mail Removed)> wrote:
>> Hello,
>> I have written a script using IO::Socket::INET to read and write data to
>> modbus devices. Using blocking IO ->read and ->write.
>> This is running on OSX V 10.4.11
>> I have the latest IO::Socket Library from CPAN.
>> ....

>
> No ideas about the problem but I'd recommend showing
> some of your code. Seeing actual code often jogs
> memories in ways other problem descriptions can't.
> What options did you pass to the IO::Socket::INET constructor,
> Reuseaddr, Timeout, Blocking, etc.?


Good point, my code is so basic that I am convinced there must be a
generic problem on OSX. I just cant believe no one else has come across
it.

I tried Reuseaddr which did not make any diferance, I am only openeing
one socket per destination anyways.

I open the sockets with
sub OpenSocket() {
#Get Socket address for IP or open a new socket
my ($IP,$PORT,$S,$R,$I,@R);
$IP=shift;
$PORT=502;

if(!$SOCKETS{$IP}) {
$S = new IO::Socket::INET (PeerAddr => $IP,
PeerPort => $PORT,
Proto => "tcp",
Timeout =>10,
Type => SOCK_STREAM) or return(undef);
$SOCKETS{$IP}=$S;
$SELECT->add($SOCKETS{$IP});
$SOCKETS{$IP}->sockopt(TCP_NODELAY,1);
};

return ($SOCKETS{$IP});
};


This is then called from for example

sub Read_10_Integer($$) {
my (@R, $IP, $ADD, $PORT,$COMMAND, $q, $socket, $len, @Reply);
$IP = shift;
$ADD = shift;
$PORT=502;
$ADD--;

$COMMAND =
"\x00\x00\x00\x00\x00\x06\x01\x03".chr(int($AD D/256)).chr($ADD %
256)."\x00\x0A";

unless($socket= OpenSocket($IP)) {
print "Can't connect ($IP) : $@ [$!]\n";
return undef;
};

print $socket $COMMAND;
$socket->read($r,6); #5th byte is the length byte *****
This is one place that it hangs with repeated retries on the tcpdump
@R=split(//,$r);
return undef if(ord($R[0])!=0 or ord($R[1])!=0);
$len=ord($R[5]);
$socket->read($r,$len);
@R=split(//,$r);
Exception(ord($R[2])) if(ord($R[1]) > 0x80);

@Reply=();
for($q=0;$q<10;$q++) {
$Reply[$q]=ord($R[$q*2+3])*256+ord($R[$q*2+4]);
};


return @Reply;
};



 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      05-08-2009
>>>>> "SG" == Stuart Gall <(E-Mail Removed)> writes:

SG> Good point, my code is so basic that I am convinced there must be a
SG> generic problem on OSX. I just cant believe no one else has come
SG> across it.

your code could still use major improvements.


SG> I open the sockets with
SG> sub OpenSocket() {
SG> #Get Socket address for IP or open a new socket
SG> my ($IP,$PORT,$S,$R,$I,@R);
SG> $IP=shift;

my( $ip ) = @_ ;

SG> $PORT=502;

my $port = 502 ;

use lower case names for lexical vars. upper case is for constants (yes,
the port is a constant but still i like lower as do most perl coders)

SG> if(!$SOCKETS{$IP}) {

unless( $sockets{$ip} ) {

where is %sockets declared? it should be passed in via a hash ref
instead of being a global

SG> $S = new IO::Socket::INET (PeerAddr => $IP,
SG> PeerPort => $PORT,
SG> Proto => "tcp",
SG> Timeout =>10,
SG> Type => SOCK_STREAM) or return(undef);

my $sock = IO::Socket::INET->new( "$ip:$port" ) ;

there, isn't that much simpler? basic tcp socket connections can pass a
single hostort string. the type, proto are defaulted
correctly. timeout is fine with its default.

$sock or return ;

no need to return undef. plain return does that when called in scalar
context.

SG> $SOCKETS{$IP}=$S;
SG> $SELECT->add($SOCKETS{$IP});

what's with all the shouting? perl style is lower case

SG> $SOCKETS{$IP}->sockopt(TCP_NODELAY,1);

here is the big question. why do you do that? it is not needed except in
very special cases and your code is not one of them. it may even be the
cause of the socket dropouts.

SG> };

SG> return ($SOCKETS{$IP});

why do you return the socket from the global hash? you have it in $S (or
in my version $sock).

SG> };




SG> This is then called from for example

SG> sub Read_10_Integer($$) {
SG> my (@R, $IP, $ADD, $PORT,$COMMAND, $q, $socket, $len, @Reply);

don't declare vars before you need them. you can my each of these in the
assignments below
SG> $IP = shift;
SG> $ADD = shift;

my( $ip, $add ) = @_ ;

shifting @_ for args is poor style. it is useful in some cases but
assiging @_ to a list of my vars is standard perl style.

SG> $PORT=502;

why is this set again? if it is really a constant, move it outside the
subs or pass it in as an arg.

SG> $ADD--;

what is $ADD? use a better name for it.

SG> $COMMAND =
SG> "\x00\x00\x00\x00\x00\x06\x01\x03".chr(int($AD D/256)).chr($ADD %
SG> 256)."\x00\x0A";


SG> unless($socket= OpenSocket($IP)) {

since this is a boolean test, have OpenSocket just return 1 on success,
not the socket itself.

SG> print "Can't connect ($IP) : $@ [$!]\n";
SG> return undef;
SG> };

SG> print $socket $COMMAND;

$socket->print( $COMMAND ) ;

SG> $socket->read($r,6); #5th byte is the length byte

$socket->sysread(my $read_buf, 6);

use sysread on sockets. unless you are doing blocking line by line
protos, you shouldn't use stdio or perlio for sockets.

SG> This is one place that it hangs with repeated retries on the tcpdump
SG> @R=split(//,$r);

my @read_bytes = split(//,$r);

SG> return undef if(ord($R[0])!=0 or ord($R[1])!=0);
SG> $len=ord($R[5]);
SG> $socket->read($r,$len);

maybe the retries are caused by the NO_DELAY thing. it moves data out in
smaller packets and those are getting lost often. remove that unhelpful
option and see what happens.

SG> @R=split(//,$r);
SG> Exception(ord($R[2])) if(ord($R[1]) > 0x80);

SG> @Reply=();

my @reply ;

SG> for($q=0;$q<10;$q++) {
SG> $Reply[$q]=ord($R[$q*2+3])*256+ord($R[$q*2+4]);
SG> };

i am sure that can be done more cleanly but i can't fathom it right now.

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Free Perl Training --- http://perlhunter.com/college.html ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
Stuart Gall
Guest
Posts: n/a
 
      05-10-2009
On 2009-05-09 01:18:45 +0300, Uri Guttman <(E-Mail Removed)> said:

Hello uri,
Thank you for such a detailed reply.
I have placed some comments in line and included my new code at the
end, with most of your suggestions.
With your suggestions (I think mainly using sysread and syswrite) the
situation is much improved.
Before I would get a lockup after 4-10 loops now it goes for 17 - 20
That is a huge improvement, but I still eventualy get a bad packet
which is apparently not correctly handled by the TCP stack on OSX or by
perl on OSX I am not sure where the handoff is.

>>>>>> "SG" == Stuart Gall <(E-Mail Removed)> writes:

>
> SG> Good point, my code is so basic that I am convinced there must be a
> SG> generic problem on OSX. I just cant believe no one else has come
> SG> across it.
>
> your code could still use major improvements.
>
>
> SG> I open the sockets with
> SG> sub OpenSocket() {
> SG> #Get Socket address for IP or open a new socket
> SG> my ($IP,$PORT,$S,$R,$I,@R);
> SG> $IP=shift;
>
> my( $ip ) = @_ ;

Like it.
>
> SG> $PORT=502;
>
> my $port = 502 ;

OK
>
> use lower case names for lexical vars. upper case is for constants (yes,
> the port is a constant but still i like lower as do most perl coders)
>
> SG> if(!$SOCKETS{$IP}) {
>
> unless( $sockets{$ip} ) {

Yes
>
> where is %sockets declared? it should be passed in via a hash ref
> instead of being a global

I did not include
sub CloseAllSockets
sub CloseSocket
%SOCKETS is a mutual static variable for those subs in a BEGIN block.

>
> SG> $S = new IO::Socket::INET (PeerAddr => $IP,
> SG> PeerPort => $PORT,
> SG> Proto => "tcp",
> SG> Timeout =>10,
> SG> Type => SOCK_STREAM) or return(undef);
>
> my $sock = IO::Socket::INET->new( "$ip:$port" ) ;
>
> there, isn't that much simpler? basic tcp socket connections can pass a
> single hostort string. the type, proto are defaulted
> correctly. timeout is fine with its default.

Perhaps, I prefer to specify specifically what I want, it is also
better when things are not working it is easier to tweak things.

>
> $sock or return ;
>
> no need to return undef. plain return does that when called in scalar
> context.

OK No need but when I am returning a value and undef on error I prefer
to write it explicitly.

>
> SG> $SOCKETS{$IP}=$S;
> SG> $SELECT->add($SOCKETS{$IP});
>
> what's with all the shouting? perl style is lower case

Well it wasn't working I thought if I shouted the computer might listen
to me a bit more )

>
> SG> $SOCKETS{$IP}->sockopt(TCP_NODELAY,1);
>
> here is the big question. why do you do that? it is not needed except in
> very special cases and your code is not one of them. it may even be the
> cause of the socket dropouts.

Well, I am communicating with real time devices using modbus, which has
short messages and I need an immediate reply. I do not want the short
messages buffered.
This line does not make any diferance to the issue I have. If it is
commented out I still have the same problem. I originaly put the line
in to try and fix the issue.

Actually I am not realy sure that this is the correct syntax.
with perl -w I get
Argument "TCP_NODELAY" isn't numeric in setsockopt at
/System/Library/Perl/5.8.6/darwin-thread-multi-2level/IO/Socket.pm line
247

Also all the packets (with data) sent from the mac have the PUSH flag
set even without TCP_NODELAY
I thought that TCP_NODELAY disables local buffering but should also set
PUSH so that the receiver does not buffer. Perhaps this is a feature of
the auto flush in IO::Socket, it sets PUSH if the packet is small and
being flushed.

>
> SG> };
>
> SG> return ($SOCKETS{$IP});
>
> why do you return the socket from the global hash? you have it in $S (or
> in my version $sock).

Answered above $SOCKETS is local static.

>
> SG> };
>
>
>
>
> SG> This is then called from for example
>
> SG> sub Read_10_Integer($$) {
> SG> my (@R, $IP, $ADD, $PORT,$COMMAND, $q, $socket, $len, @Reply);
>
> don't declare vars before you need them. you can my each of these in the
> assignments below

OK - better
> SG> $IP = shift;
> SG> $ADD = shift;
>
> my( $ip, $add ) = @_ ;
>
> shifting @_ for args is poor style. it is useful in some cases but
> assiging @_ to a list of my vars is standard perl style.

This is MUCH nicer, I have never seen it though! It is obvious now.
>
> SG> $PORT=502;
>
> why is this set again? if it is really a constant, move it outside the
> subs or pass it in as an arg.

It is left over from a time when I was opening the socket inside the
Read / write routine and immediate closing it.
I mentioned in my OP that I rewrote the code to keep the sockets open,
which then caused all my problems.

>
> SG> $ADD--;
>
> what is $ADD? use a better name for it.

OK $REGISTER

>
> SG> $COMMAND =
> SG> "\x00\x00\x00\x00\x00\x06\x01\x03".chr(int($AD D/256)).chr($ADD %
> SG> 256)."\x00\x0A";
>
>
> SG> unless($socket= OpenSocket($IP)) {
>
> since this is a boolean test, have OpenSocket just return 1 on success,
> not the socket itself.

I need the socket to read/write %SOCKET is static local

>
> SG> print "Can't connect ($IP) : $@ [$!]\n";
> SG> return undef;
> SG> };
>
> SG> print $socket $COMMAND;
>
> $socket->print( $COMMAND ) ;
>
> SG> $socket->read($r,6); #5th byte is the length byte
>
> $socket->sysread(my $read_buf, 6);
>
> use sysread on sockets. unless you are doing blocking line by line
> protos, you shouldn't use stdio or perlio for sockets.

I did not know that, this seems to have helped alot.

>
> SG> This is one place that it hangs with repeated retries on the tcpdump
> SG> @R=split(//,$r);
>
> my @read_bytes = split(//,$r);
>
> SG> return undef if(ord($R[0])!=0 or ord($R[1])!=0);
> SG> $len=ord($R[5]);
> SG> $socket->read($r,$len);
>
> maybe the retries are caused by the NO_DELAY thing. it moves data out in
> smaller packets and those are getting lost often. remove that unhelpful
> option and see what happens.

I put the option *IN* to try and correct the problem.

>
> SG> @R=split(//,$r);
> SG> Exception(ord($R[2])) if(ord($R[1]) > 0x80);
>
> SG> @Reply=();
>
> my @reply ;
>
> SG> for($q=0;$q<10;$q++) {
> SG> $Reply[$q]=ord($R[$q*2+3])*256+ord($R[$q*2+4]);
> SG> };
>
> i am sure that can be done more cleanly but i can't fathom it right now.

Probably, It is very C-ish and not very Perl-ish, but for sure it is
not the cause of the socket retry problems.
>
> uri



New code


BEGIN {
my %Sockets;

sub OpenSocket($) {
#Get Socket address for IP or open a new socket
my ($IP)=@_;
my $PORT=502;

unless ($Sockets{$IP}) {
my $S = new IO::Socket::INET (PeerAddr => $IP,
PeerPort => $PORT,
Proto => "tcp",
Timeout =>10,
Type => SOCK_STREAM) or return(undef);
$Sockets{$IP}=$S;
$SELECT->add($Sockets{$IP});
$Sockets{$IP}->sockopt(TCP_NODELAY,1);
};

return ($Sockets{$IP});
};

sub CloseAllSockets() {
foreach my $S (keys %Sockets) {
$Sockets{$S}->close();
delete $Sockets{$S};
};
$SELECT=IO::Select->new();
};

sub CloseSocket($) {
my($IP) = @_;
$SELECT->remove($SOCKETS{$IP});
$SOCKETS{$IP}->close();
delete $SOCKETS{$IP};
};

}# BEGIN BLOCK


And one of the calling functions

sub Read_10_Integer($$) {

my($IP,$Register) = @_;
$Register--;

#MBAP Header
# Bytes Meaning Value
# 2 Transaction ident - any number 00 00
# 2 Protocol Identifier - 0= Modbus 00 00
# 2 Length Number of following bytes 00 06
# 1 Unit Identifier 01
# 1 Function code 03 (Read Holding Register)
# 2 Register $REGISTER
# 2 Count 00 0A


my $COMMAND =
"\x00\x00\x00\x00\x00\x06\x01\x03".chr(int($Regist er/256)).chr($Register
% 256)."\x00\x0A";

my $socket = OpenSocket($IP);
unless($socket) {
print "Can't connect ($IP) : $@ [$!]\n";
return undef;
};

$socket->syswrite($COMMAND);
$socket->sysread(my $r,6); #5th byte is the length byte
my @R=split(//,$r);
return undef if(ord($R[0])!=0 or ord($R[1])!=0);
my $len=ord($R[5]);
$socket->sysread($r,$len);
@R=split(//,$r);
if(ord($R[1]) > 0x80) {
Exception(ord($R[2]),"READ 10 INTEGER IP=$IP ADD=$Register");
return(undef);
};
my @Reply=();
for(my $q=0;$q<10;$q++) {
$Reply[$q]=ord($R[$q*2+3])*256+ord($R[$q*2+4]);
};


return @Reply;
};

Stuart


 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      05-10-2009
>>>>> "SG" == Stuart Gall <(E-Mail Removed)> writes:

>>

SG> $S = new IO::Socket::INET (PeerAddr => $IP,
SG> PeerPort => $PORT,
SG> Proto => "tcp",
SG> Timeout =>10,
SG> Type => SOCK_STREAM) or return(undef);

>> my $sock = IO::Socket::INET->new( "$ip:$port" ) ;
>>
>> there, isn't that much simpler? basic tcp socket connections can pass a
>> single hostort string. the type, proto are defaulted
>> correctly. timeout is fine with its default.

SG> Perhaps, I prefer to specify specifically what I want, it is also
SG> better when things are not working it is easier to tweak things.

you won't need to tweak the connect call. almost no one ever does. as i
said, the protocol and type NEVER change if you want a tcp connection
and the timeout isn't critical to change.

>>
>> $sock or return ;
>>
>> no need to return undef. plain return does that when called in scalar
>> context.

SG> OK No need but when I am returning a value and undef on error I prefer
SG> to write it explicitly.

that is bad coding. if someone were to call your code in a list context,
instead of getting an empty list (which is false) they would get a
single element list containing undef (which is true). perl's return is
context sensitive and should be used with no arg for basic or empty returns.

SG> $SOCKETS{$IP}->sockopt(TCP_NODELAY,1);
>>
>> here is the big question. why do you do that? it is not needed except in
>> very special cases and your code is not one of them. it may even be the
>> cause of the socket dropouts.


SG> Well, I am communicating with real time devices using modbus, which
SG> has short messages and I need an immediate reply. I do not want the
SG> short messages buffered.
SG> This line does not make any diferance to the issue I have. If it is
SG> commented out I still have the same problem. I originaly put the line
SG> in to try and fix the issue.

it sounds like maybe your osx stack is crapola. but that isn't a perl
problem and i am sure many mac coders have written perl socket code for it.

SG> Actually I am not realy sure that this is the correct syntax.
SG> with perl -w I get
SG> Argument "TCP_NODELAY" isn't numeric in setsockopt at
SG> /System/Library/Perl/5.8.6/darwin-thread-multi-2level/IO/Socket.pm
SG> line 247

that is another problem. you didn't import the constant so it is being
passed as just a string. but i doubt you really need this.

SG> Also all the packets (with data) sent from the mac have the PUSH flag
SG> set even without TCP_NODELAY
SG> I thought that TCP_NODELAY disables local buffering but should also
SG> set PUSH so that the receiver does not buffer. Perhaps this is a
SG> feature of the auto flush in IO::Socket, it sets PUSH if the packet is
SG> small and being flushed.

autoflush has nothing to do with tcp. it is a stdio/perlio thing and if
you use syswrite it is ignored since that bypasses stdio.

>>

SG> };
>>

SG> return ($SOCKETS{$IP});
>>
>> why do you return the socket from the global hash? you have it in $S (or
>> in my version $sock).

SG> Answered above $SOCKETS is local static.

that doesn't answer why you return a hash lookup for the boolean
return. return 1 is simpler, faster and more accurate.

>>

SG> print "Can't connect ($IP) : $@ [$!]\n";
SG> return undef;
SG> };
>>

SG> print $socket $COMMAND;
>>
>> $socket->print( $COMMAND ) ;
>>

SG> $socket->read($r,6); #5th byte is the length byte
>>
>> $socket->sysread(my $read_buf, 6);
>>
>> use sysread on sockets. unless you are doing blocking line by line
>> protos, you shouldn't use stdio or perlio for sockets.

SG> I did not know that, this seems to have helped alot.

and don't mix sysread/write with other types of i/o on the sockets. that
will be living hell. always use one style or the others on any given
socket. the choice is also dependent on the data (not tcp) stream
protocol, whether you want blocking or not, how the other side is
working, etc. socket programming isn't a one sided thing.

>> maybe the retries are caused by the NO_DELAY thing. it moves data out in
>> smaller packets and those are getting lost often. remove that unhelpful
>> option and see what happens.

SG> I put the option *IN* to try and correct the problem.

and it didn't fix it so it isn't the cause. it would be unlikely to be
the cause since it is rarely used. and if your code really does work
fine on linux but not on osx, that points (but not prove) that the osx
stack is guilty. have you googled for osx and this error?


SG> BEGIN {

why the begin block? declaring empty vars subs in begin blocks does
nothing.

SG> my %Sockets;

SG> sub OpenSocket($) {

why are you using prototypes? they are meant for one thing alone
(changing how a sub call is parsed). they are not useful for arg
checking or stuff.

SG> #Get Socket address for IP or open a new socket

SG> my ($IP)=@_;
SG> my $PORT=502;

my $socket = $Sockets{$IP} ;
return $socket if $socket ;

the rest of the code is mainline, indented to the left and you save a
block. return as early when you can is a good style.

SG> unless ($Sockets{$IP}) {
SG> my $S = new IO::Socket::INET (PeerAddr => $IP,
SG> PeerPort => $PORT,
SG> Proto => "tcp",
SG> Timeout =>10,
SG> Type => SOCK_STREAM) or return(undef);
SG> $Sockets{$IP}=$S;
SG> $SELECT->add($Sockets{$IP});
SG> $Sockets{$IP}->sockopt(TCP_NODELAY,1);

you don't need that. and it isn't correct as you haven't defined the
constant. this means you aren't running strict/warnings clean. fix that
before you continue. let perl help you find bugs.


SG> sub CloseAllSockets() {
SG> foreach my $S (keys %Sockets) {
SG> $Sockets{$S}->close();
SG> delete $Sockets{$S};

values is useful there

foreach my $sock ( values %Sockets) {
$sock->close() ;
}

or even:

$_->close() for values %Sockets ;

SG> sub CloseSocket($) {
SG> my($IP) = @_;

SG> $SELECT->remove($SOCKETS{$IP});
SG> $SOCKETS{$IP}->close();
SG> delete $SOCKETS{$IP};

delete returns what it is deleting.

my $sock = delete $Sockets{$IP};
$SELECT->remove($sock);
$sock->close() ;

that saves you two hash lookups and is cleaner to read.

also you are using %SOCKETS and $Sockets which are not the same
var. strict would find that. use strict or die!!

SG> };

SG> }# BEGIN BLOCK

again, why the begin block? nothing i saw there needs to be done at
compile time.

SG> $socket->sysread(my $r,6); #5th byte is the length byte
SG> my @R=split(//,$r);
SG> return undef if(ord($R[0])!=0 or ord($R[1])!=0);
SG> my $len=ord($R[5]);
SG> $socket->sysread($r,$len);
SG> @R=split(//,$r);

SG> if(ord($R[1]) > 0x80) {
SG> Exception(ord($R[2]),"READ 10 INTEGER IP=$IP ADD=$Register");
SG> return(undef);
SG> };
SG> my @Reply=();
SG> for(my $q=0;$q<10;$q++) {

foreach my $q ( 0 .. 9 ) :

SG> $Reply[$q]=ord($R[$q*2+3])*256+ord($R[$q*2+4]);
SG> };

since you seem to need byte values use unpack to get them. much cleaner
than all those ord calls. same for building the command, pack is better
then chr calls. there is a doc called perlpacktut you can read about
pack/unpack.

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Free Perl Training --- http://perlhunter.com/college.html ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
derykus@gmail.com
Guest
Posts: n/a
 
      05-10-2009
On May 8, 2:05*pm, Stuart Gall <(E-Mail Removed)> wrote:
> On 2009-05-07 23:03:16 +0300, (E-Mail Removed) said:
>
> ...
> * * * * print $socket $COMMAND;
> * * * * $socket->read($r,6); * *#5th byte is the length byte * * *****
> This is one place that it hangs with repeated retries


Any possibility of a timeout as a workaround:

eval { local $SIG{ALRM} = sub { die 'socket t/o';
alarm(...);
$socket->read($r, 6) };
alarm(0);
};
if ( $@ =~ m{socket t/o} and not $socket->connected ) {
... reopen socket etc.
}


--
Charles DeRykus
 
Reply With Quote
 
Dr.Ruud
Guest
Posts: n/a
 
      05-11-2009
(E-Mail Removed) wrote:

> eval { local $SIG{ALRM} = sub { die 'socket t/o';
> alarm(...);
> $socket->read($r, 6) };
> alarm(0);
> };
> if ( $@ =~ m{socket t/o} and not $socket->connected ) {
> ... reopen socket etc.
> }


That template looks wrong. My go at it:

eval {
local $SIG{ALRM} = sub { die 'socket t/o' };
alarm 8;
$socket->read($r, 6);
alarm 0;
1; # success
}
or do {
my $err = $@ || "unknown";
alarm 0;
if ( $err =~ m{socket t/o} and not $socket->connected ) {
... reopen socket etc.
}
};

--
Ruud
 
Reply With Quote
 
derykus@gmail.com
Guest
Posts: n/a
 
      05-11-2009
On May 10, 6:39*pm, "Dr.Ruud" <(E-Mail Removed)> wrote:
> (E-Mail Removed) wrote:
> > eval { local $SIG{ALRM} = sub { die 'socket t/o';
> > * * * *alarm(...);
> > * * * *$socket->read($r, 6) };
> > * * * *alarm(0);
> > * * *};
> > if ( $@ =~ m{socket t/o} and not $socket->connected ) {
> > * *... *reopen socket etc.
> > }

>
> That template looks wrong. My go at it:
>
> * *eval {
> * * * *local $SIG{ALRM} = sub { die 'socket t/o' };
> * * * *alarm 8;
> * * * *$socket->read($r, 6);
> * * * *alarm 0;
> * * * *1; *# success
> * *}
> * *or do {
> * * * *my $err = $@ || "unknown";
> * * * *alarm 0;
> * * * *if ( $err =~ m{socket t/o} and not $socket->connected ) {
> * * * * * *... *reopen socket etc.
> * * * *}
> * *};
>


Much better.

Perhaps a lower-level recv/send pair might be a further improvement to
get the RST directly:

unless ( $socket->recv($r, 6) ) {
if ( $! == ECONNRESET ) {
... re-open socket, etc.
}
...

--
Charles DeRykus
 
Reply With Quote
 
Uri Guttman
Guest
Posts: n/a
 
      05-11-2009
>>>>> "d" == derykus <(E-Mail Removed)> writes:

d> Perhaps a lower-level recv/send pair might be a further improvement to
d> get the RST directly:

recv/send are just different apis from sysread/syswrite. they don't do
anything special underneath. they were intended for tcp to support data
packet boundaries. i forget the proto name but the flags are defined and
unsupported. recv/send would work with those boundaries but since they
aren't supported, they are just like sysread/write.

uri

--
Uri Guttman ------ (E-Mail Removed) -------- http://www.sysarch.com --
----- Perl Code Review , Architecture, Development, Training, Support ------
--------- Free Perl Training --- http://perlhunter.com/college.html ---------
--------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
C/C++ compilers have one stack for local variables and return addresses and then another stack for array allocations on the stack. Casey Hawthorne C Programming 3 11-01-2009 08:23 PM
Value of Un-initialized Stack Variable in OSX prateek86400@gmail.com C Programming 11 04-21-2009 08:52 PM
How do I print to a TCP/IP Printer server from Mac OSX Jack B. Pollack Computer Support 11 05-15-2005 04:52 PM
NAT two outside TCP ports to one inside TCP port Kevin Cisco 1 11-10-2004 08:15 AM
LPR/Standard, TCP/IP, HP TCP/IP Ports DJ Chiro MCSE 1 11-07-2003 08:06 PM



Advertisments