Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
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 lurking in the Monastery: (3)
As of 2025-11-14 05:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What's your view on AI coding assistants?





    Results (70 votes). Check out past polls.

    Notices?
    hippoepoptai's answer Re: how do I set a cookie and redirect was blessed by hippo!
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.