How to close a listening socket asynchronously

F

Franz Prilmeier

Hi,

I want to write a simple SMTP server (As a side note with
Net::SMTP::Server) for evaluating incoming mails. One of my objectives is
having a proper server shutdown. I will try to argue on a code example:

-- Begin Perl Code --
#! /usr/bin/perl -w

use strict;

use IO::Socket;

my $pid;
my $socket = new IO::Socket::INET->new(LocalPort => 10025,
Type => SOCK_STREAM,
Reuse => 1,
Listen => 10 )
or die "Couldn't be a tcp server on port 10025 : $@\n";
$| = 1;

FORK:
{
$pid = fork;
die "can't fork" unless defined $pid;

if ( $pid )
{
print "Child\n";
&run_server ( $socket );
print "Child shutdown finished\n";
}
else
{
print "Parent\n";
sleep 1;
print "Parent shutdown\n";
&stop_server ( $socket );
print "Parent shutdown finished\n";
exit
}
}

sub run_server
{
my $server = shift;
my $client;

while ( $client = $server-> accept () )
{
print "Incomming connection";
}

close ( $server );
}

sub stop_server
{
my $server = shift;

print "Entered Stop Server";
close ( $server );
}
-- End of perl code --

A friend of mine already told me, that $server in the child might not the
same object as $server in the parent thread. So this code example is
plain wrong, but I hope it helps to get you my intention:

I want to be able to close a socket while it is listening (waiting for
incoming connection via the accept call). In a different thread. And
definitely not by killing the whole program. I want to be able to do this
inside the same program. I haven't been able to find any proper solutions
(except connecting to the socket in the stop method - which is not what I
want to do either).

To be short: Is there a clean way to close a socket while it is listening
for incoming connections?

Thanks in advance,
Franz
 
X

xhoster

Franz Prilmeier said:
Hi,

I want to write a simple SMTP server (As a side note with
Net::SMTP::Server) for evaluating incoming mails. One of my objectives is
having a proper server shutdown.

What do you mean by a "proper server shutdown"?
I will try to argue on a code example:

Your code also doesn't tell us what you mean by a proper server shutdown.
Unless you mean that shutting down your server very second is proper, which
I highly doubt.

....
A friend of mine already told me, that $server in the child might not the
same object as $server in the parent thread.

I don't see why it wouldn't be. $socket was set before the fork.
I want to be able to close a socket while it is listening (waiting for
incoming connection via the accept call). In a different thread.

Perl doesn't to threads very well.
And
definitely not by killing the whole program. I want to be able to do this
inside the same program.

Upon what event?
I haven't been able to find any proper solutions
(except connecting to the socket in the stop method - which is not what I
want to do either).

By calling "accept", you are declaring that want to block until something
connects. If you don't want to block until something connects, don't
call "accept" until/unless you know someone is knocking on the door.

Stuff the $socket into an IO::Select object, then use can_read to see if
there is something trying to connect to $socket. (See the example given in
perldoc IO::Select).

Now that you are only checking in every now and then to see if anyone is
there, you are free to close the socket whenever you wish.
To be short: Is there a clean way to close a socket while it is listening
for incoming connections?


Xho
 
F

Franz Prilmeier

What do you mean by a "proper server shutdown"?

Stopping the server listening.
Your code also doesn't tell us what you mean by a proper server shutdown.
Unless you mean that shutting down your server very second is proper, which
I highly doubt.

I am not a perl guru, but I am definitely a Java guru. So I hope you get
my intention on a Java example (See below, SocketTest.java). This is the
output of that little program:

$ javac SocketTest.java && java SocketTest
starting server
Restarting Server:
Exception in Runner: java.net.SocketException: socket closed
starting server
Stopping Server:
Exception in Runner: java.net.SocketException: socket closed
Server exiting

That's exactly what I want to do in Perl. Stop the server listening
while it's blocked doing an accept. Is there one way to do it?
Perl doesn't to threads very well.

That would be a show stopper. I have to do that in Perl because all my
mates just know Perl.
Upon what event?

I have a second control connection either by keyboard input or by remote
command.
By calling "accept", you are declaring that want to block until something
connects. If you don't want to block until something connects, don't
call "accept" until/unless you know someone is knocking on the door.

Stuff the $socket into an IO::Select object, then use can_read to see if
there is something trying to connect to $socket. (See the example given in
perldoc IO::Select).

As far as I got IO::Select, this means busy waiting to me.

Franz

-- Begin SocketTest.java --
import java.net.*;

public class SocketTest {
public static void main ( String [] args ) throws Exception {
ServerRunner sr = new ServerRunner ( 10025 );
sr.start ();

ServerStopper st = new ServerStopper ( sr );
st.start ();
}

public static class ServerRunner extends Thread {
private ServerSocket socket = null;
private boolean shouldRun = true;
int port;

public ServerRunner ( int port ) throws Exception {
this.port = port;
}

public void run () {
while ( this.shouldRun ) {
Socket s = null;

try {
System.out.println ( "starting server" );
this.socket = new ServerSocket ( port );

while ( this.shouldRun ) {
s = this.socket.accept ();
System.out.println ( "Got a connection" );
s.close ();
}
} catch ( Exception e ) {
System.out.println ( "Exception in Runner: " + e );
} finally {
try {
if ( s != null ) s.close ();
} catch ( Exception e ) { /* Discard */ }
}
}

try {
this.socket.close ();
} catch ( Exception e ) {
System.out.println (
"Exception in Runner during Socket close " + e );
}

System.out.println ( "Server exiting" );
}

public ServerSocket getServer () {
return this.socket;
}

public void halt () {
this.shouldRun = false;
}
}

public static class ServerStopper extends Thread {
private ServerRunner server;

public ServerStopper ( ServerRunner sr ) {
this.server = sr;
}

public void run () {
try {
super.sleep ( 1000 );
System.out.println ( "Restarting Server:" );
this.server.getServer ().close ();

super.sleep ( 1000 );
System.out.println ( "Stopping Server:" );
this.server.halt ();
this.server.getServer ().close ();
} catch ( Exception e ) {
System.out.println ( "Exception in Stopper: " + e );
}
}
}
}
-- End of SocketTest.java --
 
B

Big and Blue

A suggestion (there are other ways as well...)

Get the other thread to open a call on the socket and send a "shutdown"
command. For security (to stop anyone connecting to the socket and
shuting it down) create a random string in the program, get this thread to
send it and the listener can check it.
 
F

Franz Prilmeier

A suggestion (there are other ways as well...)

Get the other thread to open a call on the socket and send a "shutdown"
command. For security (to stop anyone connecting to the socket and shuting
it down) create a random string in the program, get this thread to send it
and the listener can check it.

That would be a possible solution, and also one I thought of. Security is
no issue here, because the server is intended to be run for test purposes
only, not in a production environment (Or even on the internet). My
problem with this solution is, that it smells bad to me. It's a hack, and
I want to avoid hacks in my programs whenever possible.

I would like to try one of the other ways, preferable the one I showed in
the Java example. Is there are Perl equivalent?

Franz
 
X

xhoster

Franz Prilmeier said:
That's exactly what I want to do in Perl. Stop the server listening
while it's blocked doing an accept. Is there one way to do it?

As a previous poster suggested, using "shutdown" rather than "close" on the
socket seems to work for that. If your goal is simply to get your test
code to work, that would probably be the way to go. But presumably your
test code is a test for some other ultimate goal, and just getting this
test code to work may not be very effective at helping you attain that
ultimate goal.
That would be a show stopper. I have to do that in Perl because all my
mates just know Perl.

It may be easier to teach your mates Java than to kludge around Perl's
threading model. You may want to look into the various ways to meld
Perl and Java, so that you get the best of both worlds.
I have a second control connection either by keyboard input or by remote
command.

If "by keyboard" means the signals sent when you hit ctrl-C, then you will
want to look into SIG handlers. For remote commands, just treat those
commands as one more thing to be waited for.
As far as I got IO::Select, this means busy waiting to me.

Only if you program it that way. Stuff both the listener (e.g. $socket)
and whatever is going to send you the "stop listening" command into the
IO::Select object. Then you can call can_read without a timeout, and it
will not busy wait, it will block until one of the two things (a new
connection, or a shutdown command) happens.

Xho
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top