Problem with writing to sockets - not robust

M

megapode

Hi all,

I have a pair of programs that I use for moving text files (mostly CSV
or XML) around a WAN. I use HTTP to handle the authentication and to
move the data, so the client side program mimics some of the behaviour
of a web browser.

I want to eventually refactor the code (too much global data) but first
I want to make it more robust so that it can handle network or server
failures better.

Note: during a single session I would typically write to and then read
from the same socket. It works well when the WAN connections are good
and the server is up and running, but it doesn't handle some error
conditions well.

In particular I have a problem when sending data and Apache on the
server shuts down. This is an unusual but possible scenario - we might
be shutting the server down for maintenance or because we want to stop
data coming in for whatever reason.

The socket is opened in a subroutine...
sub openSocket{
# first set up the socket timer
$socketTimer = IO::Select->new();
unless($socket = IO::Socket::INET->new(PeerAddr => $remoteSite,
PeerPort => 80,
Proto => "tcp",
Timeout => 10,
Type => SOCK_STREAM)
){
$errorStatus = "8192";
$returnedMessage = "Can't connect to $remoteSite";
print "$localMessage$returnedMessage\n" if ($debug);
}
# add the timer if we connected
$socketTimer->add($socket) unless $errorStatus;
}

All writing to the socket is done through a subroutine too...
sub writeToSocket{
# check socket still open
if($socketTimer->can_write(30)){
syswrite($socket, $oline);
}
else
{
$errorStatus = "16384";
$returnedMessage = "Time out whilst writing to socket\n";
}
}

This is where I'm experiencing problems. The can_write(30) catches some
errors - EG if I mimic a physically broken WAN connection by unplugging
the server's ethernet cable the client program will time out after 30
seconds and will have set $errorStatus.

But in the event that I close Apache down (presumably this sends a
signla to close the socket on the server side), the client program does
not terminate gracefully. It just stops dead. By dint of putting in
some print statements I've established that the client program never
even gets to the end point of the writeToSocket routine. The program
just stops executing.

So how can I beef up this code to cope better with the circumstances
that I've described?
 
D

Damian James

I have a pair of programs that I use for moving text files (mostly CSV
or XML) around a WAN. I use HTTP to handle the authentication and to
move the data, so the client side program mimics some of the behaviour
of a web browser.

So it sounds like LWP::UserAgent would be a good place to start.
...
In particular I have a problem when sending data and Apache on the
server shuts down. This is an unusual but possible scenario - we might
be shutting the server down for maintenance or because we want to stop
data coming in for whatever reason.
...
[ snipped code based in IO::Socket::INET ]
...
But in the event that I close Apache down (presumably this sends a
signla to close the socket on the server side), the client program does
not terminate gracefully. It just stops dead. By dint of putting in
some print statements I've established that the client program never
even gets to the end point of the writeToSocket routine. The program
just stops executing.

You haven't shown where that sub is called, though, nor what you are doing to
check whether the server has closed the connection. Note that a signal sent
to the apache process on the server has nothing to do with the client. All
you'd see is the tcp connection being closed by the server. You haven't said
what you wanted your program to do in such circumstances.
So how can I beef up this code to cope better with the circumstances
that I've described?

I suggest that you examine the documentation for LWP::UserAgent. Even if
you then still feel the need to reinvent the wheel, you'll have seen what
a good, round one looks like ;-). Oh, and Lincoln Stein's _Network
Programming with Perl_ is a pretty good read. (Hi, Lincoln!)

--damian
 
M

megapode

Damian said:
So it sounds like LWP::UserAgent would be a good place to start.
Yes. But we have to deploy this code over a large number of machines
whose backups and restores, opsys upgrades and even very existence is
not within my control - so CPAN modules, whilst tempting, are not a
practical option.
You haven't shown where that sub is called, though, nor what you are doing to
check whether the server has closed the connection.
Typically it's something like
while($oline = <SOMEFILE>){
writeToSocket();
last if $errorCode;
}

Checking whether the server has closed the connection is what I don't
know how to do.
Note that a signal sent
to the apache process on the server has nothing to do with the client. All
you'd see is the tcp connection being closed by the server. You haven't said
what you wanted your program to do in such circumstances.
Sorry... you'll see that on a timeout it sets an error code. That code
is picked up in the routine that invokes writeToSocket (see above).

The idea is that the program should always output an error code when it
has terminated. A zero code means "file sent", any non-zero value means
"file not sent for some reason or another". So if the server closed the
tcp connection I'd want to detect that condition and set $errorCode to
a non-zero value.
I suggest that you examine the documentation for LWP::UserAgent. Even if
you then still feel the need to reinvent the wheel, you'll have seen what
a good, round one looks like ;-). Oh, and Lincoln Stein's _Network
Programming with Perl_ is a pretty good read. (Hi, Lincoln!)

I've heard a lot about that. Not available in the stores here - I'll
have to get one through amazon.

Thanks for the reply.
 
D

Damian James

Yes. But we have to deploy this code over a large number of machines
whose backups and restores, opsys upgrades and even very existence is
not within my control - so CPAN modules, whilst tempting, are not a
practical option.

*shrugs* Your environment, of course. You still need to have perl, of
course, and if you can have perl, usually you can have modules.
Typically it's something like
while($oline = <SOMEFILE>){
writeToSocket();
last if $errorCode;
}

Checking whether the server has closed the connection is what I don't
know how to do.
...
The idea is that the program should always output an error code when it
has terminated. A zero code means "file sent", any non-zero value means
"file not sent for some reason or another". So if the server closed the
tcp connection I'd want to detect that condition and set $errorCode to
a non-zero value.

Ah, righto. You need to catch SIGPIPE. You could set a handler like:

$SIG{PIPE} = sub { $errorCode = "Server closed connection" };

Not doing this often myself, I can't think of other ways to do it. Like I
said, I'd be looking for a module to manage this connection.

--damian
 
M

megapode

Damian said:
*shrugs* Your environment, of course. You still need to have perl, of
course, and if you can have perl, usually you can have modules.
We CAN, but it's difficult to manage because of the way things are
"run" here. Perl comes bundled with the opsys so there's no worries
about that said:
Ah, righto. You need to catch SIGPIPE. You could set a handler like:

$SIG{PIPE} = sub { $errorCode = "Server closed connection" };

Not doing this often myself, I can't think of other ways to do it. Like I
said, I'd be looking for a module to manage this connection.

Ah thanks! Fairly simple in the end. Many thanks for your help.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,576
Members
45,054
Latest member
LucyCarper

Latest Threads

Top