CompleteMoron has asked for the wisdom of the Perl Monks concerning the following question:
Hi, I'm new to perl (allthough Im pretty good with php and delphi) and have been making some small programs. I just started a program and allready am stuck. I am trying to make a server/client app. All that th server does is accept connections, and on reading data from a client, sends it to every other one. The client should connect to the server, receive data from it, and send data to it, a very very simple chat application in short. I am having problems with the server, to manage the different connections, I am using the select() function, as described here:
http://www.perlfect.com/articles/select.shtml
My code allmost exactly the same as that, and it still deosnt work, for a start I would like it to print the data received from the clien on the screen, what is wrong here (it never prints the data to the screen)?:
#!/usr/bin/perl
#test script
use IO::Socket;
use IO::Select;
if(@ARGV < 1)
{
printf("Usage:\ntest.pl <port to run on>\n");
exit();
}
my $sock = IO::Socket::INET->new(
Proto=>"tcp",
LocalHost=>"Localhost",
Listen=>16,
Reuse=>1,
LocalPort=>$ARGV[0]
) or die("Could not create socket!\n")
+;
my $readSet = new IO::Select();
$readSet->add($sock);
while(1)
{
my $rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach $rh(@rhSet)
{
if($rh == $sock)
{
$newSocket=$rh->accept();
$readSet->add($newSocket);
} else
{
$buf=<$rh>;
if($buf)
{
printf "$buf";
} else
{
$readSet->remove($rh);
close($rh);
}
}
}
}
I have allready anticipated another problem I will have. As you see, it accepts any new sockets, but what if, I want to send data to all open sockets the server has received? I see no where (array, etc) where the sockets are saved. Any help?
Re: New to perl: IO Select question :(
by Roger (Parson) on Aug 21, 2005 at 00:00 UTC
|
I think the most likely problem in your code is here:
my $rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach $rh(@rhSet)
{
Should it be my @rhSet = ...? Your @rshSet was never set in your script. It's a common error for new Perl programmers. You can catch these kinds of errors if you turn on the strict pragma with use strict; at the top of your code.
I recommend to use the cpan module Net::Server to write your server. Unless you are learning socket level programming, why bother with low level stuff when you have a well written module that does pretty much everything you want?
| [reply] [d/l] [select] |
|
| [reply] |
|
Thanks for the reply. Making it an array (@) still doesnt make a difference :( Yeh when I was reading a perl tutorial they mentioned that all variables must be declared with a "my" infront of them, but then reading the select() tutorial, some variables used my, some didnt so that confused me, I just copied the code.
Any more suggestions?
**EDIT** Yes, i am learning network programming, but il take a look at net::server thanks
| [reply] |
|
Try this instead, in case the select method returns a reference to an array:
my $rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach $rh(@$rhSet)
{
You should also read the perldoc for IO::Select, there is a short example at the end.
perldoc IO::Select
EXAMPLE
Here is a short example which shows how "IO::Select" could
be used to write a server which communicates with several
sockets while also listening for more connections on a
listen socket
use IO::Select;
use IO::Socket;
$lsn = new IO::Socket::INET(Listen => 1, LocalPort => 8080);
$sel = new IO::Select( $lsn );
while(@ready = $sel->can_read) {
foreach $fh (@ready) {
if($fh == $lsn) {
# Create a new socket
$new = $lsn->accept;
$sel->add($new);
}
else {
# Process socket
# Maybe we have finished with the socket
$sel->remove($fh);
$fh->close;
}
}
}
| [reply] [d/l] [select] |
|
|
Re: New to perl: IO Select question :(
by pg (Canon) on Aug 21, 2005 at 00:32 UTC
|
You never had the opportunity to accept any connection from the client side. Add $sock to IO::Select is no good, as it is not an "active channel" yet. Add the accept(), plus fix some minor syntax stuff, your code works.
use strict;
use warnings;
use IO::Socket;
use IO::Select;
my $sock = IO::Socket::INET->new(
Proto=>"tcp",
LocalHost=>"localhost",
Listen=>16,
Reuse=>1,
LocalPort=>3000
) or die("Could not create socket!\n")
+;
my $connection = $sock->accept();
my $readSet = new IO::Select();
$readSet->add($connection);
while(1) {
my @rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach my $rh (@{$rhSet[0]}) {
my $buf=<$rh>;
if($buf)
{
printf "$buf";
} else
{
$readSet->remove($rh);
close($rh);
}
}
}
Client side test code:
use IO::Socket;
my $sock = IO::Socket::INET->new(
Proto=>"tcp",
PeerHost=>"localhost",
PeerPort=>3000
) or die("Could not create socket!\n")
+;
print $sock "abcd\n";
Well, I leave it to you to add back the code to accept further connections. | [reply] [d/l] [select] |
|
my @rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach my $rh (@{$rhSet[0]}) {
with (as in your example)
my @rhSet = IO::Select->select($readSet, undef, undef, 0);
foreach my $rh (@{$rhSet[0]}) {
The
my $connection = $sock->accept();
$readSet->add($connection);
you added are not needed, and I'd personally consider doing that more harmful than useful.
I also don't understand your comment about $sock not being an "active channel". After you create a server socket you can add it to the read set perfectly fine, and the select will return the socket when there's a connection to accept. | [reply] [d/l] [select] |
|
For server side, one cares whether each connection can read or write, not the listening server socket. The listening socket does not read or write.
Let's do a simple testing. With the following client and server:
#server
use strict;
use warnings;
use IO::Socket;
use IO::Select;
my $sock = IO::Socket::INET->new(Proto=>"tcp", LocalHost=>"localhost",
+
Listen=>16, Reuse=>1, LocalPort=>3000)
|| die("Could not create socket!\n");
my $conn = $sock->accept();
my $sel = new IO::Select($sock);
if ($sel->can_read()) {
print "can read\n";
}
#client
use IO::Socket;
my $sock = IO::Socket::INET->new(
Proto=>"tcp",
PeerHost=>"Localhost",
PeerPort=>3000
) or die("Could not create socket!\n")
+;
print $sock "abcd\n";
Run perl -w server.pl in one window, then run perl -w client.pl in another window, nothing gets printed in the server window! run perl -w client.pl again, "abcd" gets printed. This is not what one will want.
change server code:
use strict;
use warnings;
use IO::Socket;
use IO::Select;
my $sock = IO::Socket::INET->new(Proto=>"tcp", LocalHost=>"localhost",
+
Listen=>16, Reuse=>1, LocalPort=>3000)
|| die("Could not create socket!\n");
my $conn = $sock->accept();
my $sel = new IO::Select($conn);
if ($sel->can_read()) {
print "can read\n";
}
The first time you run perl -w client.pl, server side knows that it can read, and prints "abcd". That's what one wants. | [reply] [d/l] [select] |
|
Re: New to perl: IO Select question :(
by zentara (Archbishop) on Aug 21, 2005 at 12:39 UTC
|
In case you havn't found the answer yet, here is a working script for you to test. If the script below dosn't work, you may have poor clients. I included a compatible client below the server.
Server:
#!/usr/bin/perl
use IO::Socket;
use IO::Select;
my @sockets;
my $machine_addr = '192.168.0.1';
$main_sock = new IO::Socket::INET(LocalAddr=>$machine_addr,
LocalPort=>1200,
Proto=>'tcp',
Listen=>3,
Reuse=>1,
);
die "Could not connect: $!" unless $main_sock;
print "Starting Server\n";
$readable_handles = new IO::Select();
$readable_handles->add($main_sock);
while (1)
{
($new_readable) = IO::Select->select($readable_handles, undef, undef
+, 0);
foreach $sock (@$new_readable)
{
if ($sock == $main_sock)
{
$new_sock = $sock->accept();
$readable_handles->add($new_sock);
}
else
{
$buf = <$sock>;
if ($buf)
{
print "$buf\n";
my @sockets = $readable_handles->can_write();
#print $sock "You sent $buf\n";
foreach my $sck(@sockets){print $sck "$buf\n";}
}
else
{
$readable_handles->remove($sock);
close($sock);
}
}
}
}
print "Terminating Server\n";
close $main_sock;
getc();
Client: #######################################
#!/usr/bin/perl -w
use strict;
use IO::Socket;
my ( $host, $port, $kidpid, $handle, $line );
( $host, $port ) = ('192.168.0.1',1200);
my $name = shift || '';
if($name eq ''){print "What's your name?\n"}
chomp ($name = <>);
# create a tcp connection to the specified host and port
$handle = IO::Socket::INET->new(
Proto => "tcp",
PeerAddr => $host,
PeerPort => $port
)
or die "can't connect to port $port on $host: $!";
$handle->autoflush(1); # so output gets there right away
print STDERR "[Connected to $host:$port]\n";
# split the program into two processes, identical twins
die "can't fork: $!" unless defined( $kidpid = fork() );
# the if{} block runs only in the parent process
if ($kidpid) {
# copy the socket to standard output
while ( defined( $line = <$handle> ) ) {
print STDOUT $line;
}
kill( "TERM", $kidpid ); # send SIGTERM to child
}
# the else{} block runs only in the child process
else {
# copy standard input to the socket
while ( defined( $line = <STDIN> ) ) {
print $handle "$name->$line";
}
}
I'm not really a human, but I play one on earth.
flash japh
| [reply] [d/l] [select] |
|
|