radagast has asked for the wisdom of the Perl Monks concerning the following question:
I am working on a peer-peer chat system (which later should involve some interesting encryption). However, now I'm only doing the basics. I've set up one side to be a server and am using the Telnet in Windows to connect to the script on my unix box. My server is the following:
#!/usr/bin/perl
use IO::Socket;
$SIG{CHLD} = sub {wait()};
$main_sock = new IO::Socket::INET( LocalHost => 'b5',
LocalPort => 6666,
Listen => 5,
Proto => 'tcp',
Reuse => 1) or die $!;
$line = "";
while ($new_sock = $main_sock->accept()) {
$pid = fork();
die "Cannot fork: $!" unless defined($pid);
if ($pid == 0) {
# Child process
while (defined ($buf = <$new_sock>)) {
print "Client said: $buf\n";
}
exit(0);
} else {
$line = <STDIN>;
print $new_sock "$line";
}
}
My question is with regards to non-blocking. When I first connect, I can write from my telnet session to my server no problem but I can only send one thing I type from my server. Somehow it is not getting to the print $new_sock "$line"; statement. I'm doing forking because it seemed less hairier than using IO::Select. This code has been borrowed and modified from Advanced Perl Programming.
Re: Sockets
by Fastolfe (Vicar) on Nov 03, 2000 at 18:58 UTC
|
If you want to go with a forking model, the goal is for each thread to handle communications to/from the client. This way each thread has full state information about its individual client. For that reason, you probably want to set up some pipes as you fork off your children, and have the parent intercommunicate with the children using those pipes (instead of sending directly to the socket).
Basically let the parent manage the children, let it pass information between the children, and let the children do everything related to communicating with the clients.
The thing is, if you're forking already, do you really need non-blocking sockets? (Note: Your code isn't using non-blocking sockets as it is now.) In any event, when you're doing real-time interaction like this, you want to turn off buffering:
$|=1;
# and perhaps after your accept() call:
$new_sock->autoflush;
Once you get this working, you might consider trying to re-write it using IO::Select, using non-blocking sockets, via something like this:
my $res = $sock->fcntl(F_GETFL, 0);
$sock->fcntl(F_SETFL, $res | O_NONBLOCK);
Getting something fully robust is going to take a lot more coding than you're doing now. You'll learn a lot in the progress, however. Good luck. | [reply] [d/l] [select] |
|
If you want to go with a forking model, the goal is for each thread to handle communications to/from the client. This way each thread has full state information about its individual client. For that reason, you probably want to set up some pipes as you fork off your children, and have the parent intercommunicate with the children using those pipes (instead of sending directly to the socket).
I'm interested in setting up pipes as the kids are forked! What manpages/books are best for reading how to do this? Could you cook up a code snippet or direct me to one?
| [reply] |
RE: Sockets
by Corion (Patriarch) on Nov 03, 2000 at 18:27 UTC
|
I have written a non-forking non-blocking server, which
is available in the Code Vaults as Push HTTP Server.
This is an implementation of a server that notifies all clients
when data changes, and all in one thread.
The protocol is some mutated http protocol,
so I was able to test it with both, telnet and Internet
Explorer.
| [reply] |
RE: Sockets
by clemburg (Curate) on Nov 03, 2000 at 18:39 UTC
|
Turn on autoflushing. It works OK like this on my NT box (fork() is there, too - kudos to ActiveState for fork implementation!).
#!/usr/bin/perl
use IO::Socket;
$SIG{CHLD} = sub {wait()};
# turn autoflushing on
$| = 1;
$main_sock = new IO::Socket::INET( LocalHost => '127.0.0.1',
LocalPort => 6666,
Listen => 5,
Proto => 'tcp',
Reuse => 1) or die $!;
$line = "";
while ($new_sock = $main_sock->accept()) {
$pid = fork();
die "Cannot fork: $!" unless defined($pid);
if ($pid == 0) {
# Child process
while (defined ($buf = <$new_sock>)) {
print "Client said: $buf\n";
}
exit(0);
} else {
$line = <STDIN>;
print $new_sock "$line";
}
}
NOTE: (added to node later) - sorry - I did not get your point right - the script does not remedy the problem you talked about - however, turning autoflushing on is a good idea for networking code, anyway.
Christian Lemburg
Brainbench MVP for Perl
http://www.brainbench.com | [reply] [d/l] |
|
Curious, what did this snippet do for you?
$SIG{CHLD} = sub {wait()};
and what about this part?
} else {
$line = <STDIN>;
print $new_sock "$line";
}
Also, how do you get the server to hang up on the client? I tried something like:
chomp($buf);
exit(0) if ($buf eq 'q');
But the client (I use telnet) connected to the server holds fast until I hit <CR> on the console that's running the server. | [reply] [d/l] [select] |
|
Well, I just copied the code over from the question
to illustrate, but ...
- $SIG{CHLD} = sub {wait()};
This ensures
proper cleanup of child resources after child terminates.
"When a process exits, its parent is sent a CHLD signal
by the kernel and the process becomes a zombie until
the parent calls wait or waitpid.
If you start another process in Perl using anything except fork, Perl takes care of reaping your zombied children, but if you use a raw fork, you're expected to clean up after yourself." (from Camel, 3rd ed.).
I don't know really if this is needed on Win32, but
it is supported, since "wait() and waitpid() can be passed a pseudo-process ID returned by fork(). These calls will properly wait for the termination of the pseudo-process and return its status." (from ActiveState Perl docs).
- The else clause is the main server process.
It blocks reading one line from STDIN and sends that to the newly forked socket connection.
This is probably not the best example code for writing
servers and clients. Have a look at the Perl Cookbook,
chapter 17.
Christian Lemburg
Brainbench MVP for Perl
http://www.brainbench.com
| [reply] [d/l] [select] |
|
Re: Sockets
by fundflow (Chaplain) on Nov 03, 2000 at 18:28 UTC
|
It seems okay and works here (Solaris 2.6).
Maybe try a different telnet client.
It doesn't accept multiple clients though.
BTW, since you are doing only the server side, why do you
implement a client side as well?
Two other small comments:
1. You don't need the $line=""
2. You can remove the 'defined' | [reply] [d/l] |
Re: Sockets
by meonkeys (Chaplain) on Apr 24, 2001 at 13:51 UTC
|
Curious, what did this snippet do for you?
$SIG{CHLD} = sub {wait()};
and what about this part?
} else {
$line = <STDIN>;
print $new_sock "$line";
}
Also, how do you get the server to hang up on the client? I tried something like:
chomp($buf);
exit(0) if ($buf eq 'q');
But the client (I use telnet) connected to the server holds fast until I hit <CR> on the console that's running the server. | [reply] [d/l] [select] |
|
|