Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

TCP Client/Server handling multiple Client(s) connections, problem with multiple Nicknames

by thanos1983 (Parson)
on Jun 13, 2014 at 00:56 UTC ( [id://1089749]=perlquestion: print w/replies, xml ) Need Help??

thanos1983 has asked for the wisdom of the Perl Monks concerning the following question:

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!
  • Comment on TCP Client/Server handling multiple Client(s) connections, problem with multiple Nicknames
  • Select or Download Code

Replies are listed 'Best First'.
Re: TCP Client/Server handling multiple Client(s) connections, problem with multiple Nicknames
by Anonymous Monk on Jun 13, 2014 at 06:35 UTC

    You can use a hash. I wrote some example code to show how you can use it.

    my %Nicks; # global variable # you accept the NICK, then $Nicks{$client} = $nick; # later on, when you get a message $client->print("$Nicks{$client} $msg") # NICK message # when the client disconnects delete $Nicks{$client};

      To: Anonymous Monk,

      Thanks a lot I was thinking somehow to combine it with a hash. But I was not really sure how to do it. I will have a go thanks again for your time and effort.

      Seeking for Perl wisdom...on the process...not there...yet!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1089749]
Approved by Jim
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2025-06-17 01:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.