$SIG{PIPE} = sub {close $active; exit (3)};
sub readn
{
my ($socket, $bytes ) = @_;
my $offset = 0;
my $buf = "";
while ($offset < $bytes)
{
my $nread;
my $nleft = $bytes-$offset;
$nread = sysread($socket,$buf,$nleft,$offset);
kill 'PIPE',$$ unless (defined $nread); ## undef is like -1 unix return
last if ($nread ==0); ## EOF
$offset += $nread;
}
return ($buf,$offset);
}
sub writen
{
my ($socket, $buf) = @_;
my $bytes = length $buf;
my $offset =0;
while ($offset < $bytes)
{
my $nwritten;
my $nleft = $bytes-$offset;
$nwritten = syswrite($socket,$buf,$nleft,$offset);
kill 'PIPE',$$ unless (defined $nwritten); # undef is like -1 unix return
$offset += $nwritten;
}
return $offset;
}
####
#!/usr/bin/perl -w
use strict;
use IO::Socket;
use POSIX ":sys_wait_h";
my $RW_BUF_LEN = 256;
my $SERVER_TIMEOUT_SECS = 20;
my $active;
#note SOMAXCONN is system max of queue for incoming clients
#Listen=>1 would have also have been just fine
$SIG{PIPE} = sub {close $active; exit (3)};
$SIG{ALRM} = sub {close $active; exit (7)};
$SIG{CHLD} = sub {local ($!, $?); while (waitpid(-1, WNOHANG) > 0){} };
my $passive = IO::Socket::INET->new( Proto => 'tcp',
LocalPort => 12455,
Listen => SOMAXCONN,
Reuse => 1,
Type => SOCK_STREAM
)
or die "Server is unable to start: $!\n";
while(1)
{
$active = $passive->accept() or next; # next not really necessary
# Perl > 5.8 has deferred signals by default on
# so a signal cannot occur within accept()!
die "Bad Fork! $!\n" if ( !defined(my $pid = fork()) );
if ($pid != 0) #Parent note: Windows has a negative $pid (fork emulation)
{
close $active; #parents do not talk to anybody
next;
}
####### we are the child #########
close $passive; #Client's don't listen for connections!
my %whoData;
open (WHO, "who|") or die "can't open pipe from who command $!\n";
while ( my $line = )
{
my ($user) = $line =~ m/^(\w+)/;
push @{$whoData{$user}}, $line;
}
close WHO;
my $buf;
while(alarm($SERVER_TIMEOUT_SECS), ($buf) = readn($active, $RW_BUF_LEN),
$buf !~ m/^\*quit/ )
{
alarm(0);
if ($buf =~ m/^\*all/)
{
DumpAllWhoData($active,\%whoData);
next;
}
my ($username) = $buf =~ m/^(\w+)/;
if (exists $whoData{$username})
{
foreach my $line ( @{$whoData{$username}} )
{
SendStringToClient($active,$line);
}
SendStringToClient($active,"\01");
}
else
{
SendStringToClient ($active, "username not found!\n");
SendStringToClient ($active, "\01");
}
}
close $active;
exit(0); #prevent grandchildren! #### Very important!!!
}
sub readn
{
my ($socket, $bytes ) = @_;
my $offset = 0;
my $buf = "";
while ($offset < $bytes)
{
my $nread;
my $nleft = $bytes-$offset;
$nread = sysread($socket,$buf,$nleft,$offset);
kill 'PIPE',$$ unless (defined $nread); ## undef is like -1 unix return
last if ($nread ==0); ## EOF
$offset += $nread;
}
return ($buf,$offset);;
}
sub writen
{
my ($socket, $buf) = @_;
my $bytes = length $buf;
my $offset =0;
while ($offset < $bytes)
{
my $nwritten;
my $nleft = $bytes-$offset;
$nwritten = syswrite($socket,$buf,$nleft,$offset);
kill 'PIPE',$$ unless (defined $nwritten); # undef is like -1 unix return
$offset += $nwritten;
}
return $offset;
}
sub DumpAllWhoData
{
my ($socket, $href) = @_;
foreach my $user (keys %$href)
{
foreach my $line ( @{$href->{$user}} )
{
SendStringToClient($socket,$line);
}
}
SendStringToClient($socket,"\01");
}
sub SendStringToClient
{
my ($socket, $string) = @_;
my $pad = $RW_BUF_LEN - length $string;
my $buf = $string.pack("x$pad");
writen ($socket,substr($buf,0,1));
writen ($socket,substr($buf,1,$RW_BUF_LEN-1));
}
####
#!/usr/bin/perl -w
use strict;
use IO::Socket;
my $RW_BUF_LEN = 256;
my $TERMINATOR = 01; #CTRL-A
$SIG{'PIPE'} = sub{print "Server Disconnected! Write to server failed!\n"; exit(1);};
$SIG{'INT'} = \&terminal_handler;
$SIG{'QUIT'} = \&terminal_handler;
$SIG{'USR1'} = sub{print "Communications Error - read from server failed!\n"; exit(3);};
my ($socket) = IO::Socket::INET->new(
PeerAddr => 'voyager.deanza.edu',
PeerPort => 12455,
Proto => 'tcp',
Type => SOCK_STREAM
)
|| die "Cannot connect to server! $!\n";
my $input;
while ( (print "Enter username (or *quit): "),
($input = ) !~ m/^\s*\*quit\s*$/)
{
next if $input =~ /^\s*$/; # blank line, re-prompt
my ($username, $kruft) = split /\s+/, $input;
if ($kruft)
{
print "Only one username per line allowed!\n";
next;
}
my $pad = $RW_BUF_LEN - length ($username);
my $buf = $username.pack("x$pad"); # pads $buf with null \000 chars
#print "length of username packet=",length $buf,"\n"; # for debug
writen ($socket,substr($buf,0,1));
writen ($socket,substr($buf,1,$RW_BUF_LEN-1));
open (my $more, "|more") || die "cannot open pipe to more! $!\n";
while (($buf) = readn($socket, $RW_BUF_LEN),
$TERMINATOR != ord(unpack("a1",$buf)) )
{
my ($temp) = $buf =~ m/^(.*?\n)/; #needed for "more"
#regular "print $buf;" is fine
#but trailing nulls drive "more" crazy!
print $more $temp;
}
close $more;
}
send_quit_packet(); #tell server we are "hanging up"
close $socket;
exit(0);
##################
sub terminal_handler
{
my $signame = shift;
print "\nClient: received signal: $signame\n"; #debugging statement
print "Client: Disconnecting from server!\n"; #debugging statement
send_quit_packet(); #say, "bye, bye"
close $socket;
exit(2);
}
sub send_quit_packet
{
my $pad = $RW_BUF_LEN - length ('*quit');
my $buf = '*quit'.pack("x$pad");
#print "length of quit packet buffer = ",length $buf,"\n"; #debug
writen ($socket,substr($buf,0,1));
writen ($socket,substr($buf,1,$RW_BUF_LEN-1));
}
sub readn
{
my ($socket, $bytes ) = @_;
my $offset = 0;
my $buf = "";
while ($offset < $bytes)
{
my $nread;
my $nleft = $bytes-$offset;
$nread = sysread($socket,$buf,$nleft,$offset);
kill 'USR1',$$ unless (defined $nread); ## undef is like -1 unix return
last if ($nread ==0); ## EOF
$offset += $nread;
}
# print "length of received buff=",length $buf,"\n";
# print $buf;
return $buf;
}
sub writen
{
my ($socket, $buf) = @_;
my $bytes = length $buf;
my $offset = 0;
while ($offset < $bytes)
{
my $nwritten;
my $nleft = $bytes-$offset;
$nwritten = syswrite($socket,$buf,$nleft,$offset);
kill 'PIPE',$$ unless (defined $nwritten); # undef is like -1 unix return
$offset += $nwritten;
}
#print "num bytes written to server: $offset\n";
return $offset;
}
####
writen ($socket,substr($buf,0,1));
writen ($socket,substr($buf,1,$RW_BUF_LEN-1));