Dear Monks,
Thanks in advance for your time and effort reading and replying to my question.
I have created my first TCP chat Server multiple client using the Select() function. Well the program seems to be working just fine.
The problem is that I am trying to differentiate the Nicknames. In cases that I have more than one client communicating I can not print their Nicknames of all, I can only use the first Nickname. Based on my configuration I store the first Nickname and then I use it for all clients as Nickname.
I was thinking about using an array but if would not give a reference point based on users. At this point I run out of options. My idea was that I need a reference somehow on the Server storing the Client name and when this client is communicating use his nickname.
My communication sequence process of new connections is displayed under:
Client Server
Connect() -- >
< -- Hello version
NICK nick -- >
< -- OK/ERROR text
MSG text -- >
< -- MSG nick text/ERROR text
Any body has any ideas how to keep track of the Nicknames?
Working code for Client.pl is provided under:
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
use Data::Dumper;
use IO::Socket::INET;
use constant ARGUMENTS => scalar 2;
use constant NICKNAME => scalar 12;
use constant MAXBYTES => scalar 255;
# flush memory after every initialization
$| = 1;
my $info = $ARGV[0]; # User message argv[0]
my $Nickname = $ARGV[1]; # User nickname argv[1]
my ( $kidpid, $line , $client_sock , $server_data , $send );
my $error = 'ERROR';
my $newline = "\n";
if (@ARGV > ARGUMENTS) {
print "\nPlease no more than ".ARGUMENTS." arguments (ARGV[])!\n";
print "\nCorrect Syntax: perl $0 'IP:PORT NICKNAME' (e.g. 127.0.0
+.1:5000 Thanos)\n\n";
exit();
}
elsif (@ARGV < ARGUMENTS) {
print "\nPlease no less than ".ARGUMENTS." arguments (ARGV[])\n";
print "\nCorrect Syntax: perl $0 'IP:PORT NICKNAME' (e.g. 127.0.0
+.1:5000 Thanos)\n\n";
exit();
}
else {
my $string = index($info, ':');
if ($string == '-1') {
die "Please add ':' in your input - ".$info."\n";
}
my @input = split( ':' , $info );
# create a tcp connection to the specified host and port
$client_sock = IO::Socket::INET->new( Proto => "tcp",
PeerAddr => $input[0],
PeerPort => $input[1]
) or die "Can't connect to port ".$input[1]." at ".$input[0].": $!
+\n";
$client_sock->autoflush(1); # so output gets there right away
print STDERR "[Connected to ".$input[0].":".$input[1]."]\n";
$line = <$client_sock>;
my $receive = &receive($line);
#print "First receive: ".$receive."\n";
if ($receive eq "Hello version") {
$Nickname = "NICK ".$Nickname."";
$send = &send($Nickname);
#print "First send: ".$Nickname."\n";
$line = <$client_sock>;
$receive = &receive($line);
#print "Second receive: ".$receive."\n";
if ($receive eq "OK") {
# split the program into two processes, identical twins
print "Client '".$ARGV[1]."' enter your text here:\n";
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 = <$client_sock> ) ) {
$receive = &receive($line);
print "Third receive: ".$receive."\n";
print "Client '".$ARGV[1]."' enter your text here:\n";
} # End of While reading (parent)
} # End of if (parent)
# the else{} block runs only in the child process
else {
# copy standard input to the socket
while ( defined( $line = <STDIN> ) ) {
chomp ($line);
my $line = "MSG ".$line."";
$send = &send($line);
if ($line =~ /quit|exit/i) {
$line = "Client request ".$line."";
my $send = &send($line);
kill( "TERM", $kidpid ); # send SIGTERM to child
}
} # End of read and send
} # End of else child
} # End of if (OK)
else {
print "Did not Receive OK!\n";
exit();
}
} # End of if (Hello version)
else {
print "Did not receive Hello version!\n";
exit();
}
} # End of else @ARGV
sub send {
$_[0] = "".$_[0]."".$newline."";
utf8::encode($_[0]);
print $client_sock $_[0];
chomp($_[0]);
#print "The following data send to Server: (\ ".$_[0]." \)\n";
#$client_sock->send($client_packet,MAXBYTES);
return $_[0];
}
sub receive {
# we can read from socket through recv() in IO::Socket::INET
#$client_sock->recv($server_data,MAXBYTES);
utf8::decode($_[0]);
chomp($_[0]);
#print STDOUT "The following data received form Server: (\ ".$_[0]
+." \)\n";
return $_[0];
}
Working code for Server.pl is provided under:
#!/usr/bin/perl
use utf8;
use strict;
use warnings;
use IO::Select;
use Data::Dumper;
use IO::Socket::INET; # Non-blocking I/O concept.
use constant ARGUMENTS => scalar 1;
use constant NICKNAME => scalar 12;
use constant MAXBYTES => scalar 255;
# flush memory after every initialization
$| = 1;
my $info = $ARGV[0]; # User message IP:PORT;
my $error = "ERROR";
my $newline = "\n";
my ( $client_data , $server_sock , $buf , $sock , $receive , $new_sock
+ , $send , $readable_handles , $nick );
if (@ARGV > ARGUMENTS) {
print "\nPlease no more than ".ARGUMENTS." input!\n";
print "\nCorrect Syntax: perl $0 IP:PORT (e.g. 127.0.0.1:5000)\n";
exit();
}
elsif (@ARGV < ARGUMENTS) {
print "\nPlease no less than ".ARGUMENTS." input!\n";
print "\nCorrect Syntax: perl $0 IP:PORT (e.g. 127.0.0.1:5000)\n";
exit();
}
else {
my $string = index($info, ':');
if ($string == '-1') {
die "Please include ':' in your input - ".$info."\n";
}
my @input = split( ':' , $info );
$server_sock = new IO::Socket::INET( LocalAddr => $input[0],
LocalPort => $input[1],
Proto => 'tcp',
Listen => SOMAXCONN,
Reuse => 1 ) or die "Could not connect: $!";
print "\n[Server $0 accepting clients at PORT: ".$input[1]." and I
+P: ".$input[0]."]\n";
$readable_handles = new IO::Select();
$readable_handles->add($server_sock);
while (1) {
(my $new_readable) = IO::Select->select($readable_handles, undef,
+undef, 0);
# conver string to array @$new_readable
foreach $sock (@$new_readable) {
# Check if sock is the same with server (e.g. 5000)
# if same (new client) accept client socket
# else read from socket input
if ($sock == $server_sock) {
$new_sock = $sock->accept()
or die sprintf "ERROR (%d)(%s)(%d)(%s)", $!,$!,$^E,$^E;
$readable_handles->add($new_sock);
$send = "Hello version";
$client_data = &send($send);
print "First send: ".$client_data."\n";
}
else {
$buf = <$sock>;
$receive = &receive($buf);
print "First receive: ".$receive."\n";
my @text = split(/ / , $receive , 2);
#print Dumper(@text);
if ($text[0] eq "NICK") {
if (length($text[1]) > NICKNAME) {
$send = "".$error." Please no more than ".NICKNAME." chara
+cters as nickname!";
$client_data = &send($send);
$readable_handles->remove($sock);
close($sock);
}
elsif ($text[1] =~ s/\W//g) {
$send = "".$error." Special characters detected in the nic
+kname, please remove them!";
$client_data = &send($send);
$readable_handles->remove($sock);
close($sock);
}
else {
$nick = $text[1];
$send = "OK";
$client_data = &send($send);
print "Second send: ".$client_data."\n";
}
} # End of if ($text[0] eq "NICK")
elsif ($text[0] eq "MSG") {
if (length($text[1]) > MAXBYTES) {
$send = "".$error." Please remember that message limit is
+".MAXBYTES."";
$client_data = &send($send);
print "In case of message over ".MAXBYTES." send: ".$clien
+t_data."\n";
}
else {
print "Second receive: ".$text[1]."\n ";
# Get all client(s) socket(s)
my @sockets = $readable_handles->can_write();
# Send the same message to client(s)
foreach my $sck (@sockets) {
my $final = "".$text[0]." ".$nick." ".$text[1]."";
utf8::encode($final);
print $sck "".$final."".$newline."";
print "Third send: ".$final."\n";
#print STDOUT "The following data send to Client(s): (
+\ ".$buf." \)\n";
} # End of foreach
}
} # End of elsif ($text[0] eq "MSG")
else {
print "Closing client!\n";
$readable_handles->remove($sock);
close($sock);
} # End of else condition
} # End of else condition ($sock == $server_sock)
} # End of foreach new sock
} # End of While (1)
print "Terminating Server\n";
close $server_sock;
getc();
} # End of else @ARGV
sub send {
$_[0] = "".$_[0]."".$newline."";
utf8::encode($_[0]);
print $new_sock $_[0];
chomp ($_[0]);
#print "The following data send to Cliets: (\ ".$_[0]." \)\n";
#$client_sock->send($client_packet,MAXBYTES);
return $_[0];
}
sub receive {
#$new_sock->recv($client_data,MAXBYTES);
utf8::decode($_[0]);
chomp ($_[0]);
if($_[0] =~ /^$/) {
print "Data packet received empty!\n";
print "From host: ".$new_sock->peerhost()." and port: ".$new_sock-
+>peerport()."\n";
return $_[0];
}
elsif ($_[0] !~ /^$/) {
#print STDOUT "The following data received from Client: (\ ".$buf.
+" \)\n";
#print "From host: ".$new_sock->peerhost()." and port: ".$new_sock
+->peerport()."\n";
return $_[0];
}
else {
$error = "".$error."".$newline."";
utf8::encode ($error);
$server_sock->send($error);
print "Invalid client: ".$new_sock->peerhost()." terminating!\n";
$readable_handles->remove($sock);
close($sock);
}
}
Again thanks everyone time and effort to help me with my problem.
Seeking for Perl wisdom...on the process...not there...yet!