in reply to How to retrieve the port number on a Multiple TCP Chat Client Server
Hi Thanos1983
I have been looking at your server code, as I am learning about ipc and sockets. So I have not got a complete answer, but a few possible hints which I hope can help. The client code helped greatly.
I cleaned up some of the newlines and concatanations so I could see what it is you were trying to do better. So here are some things that I spotted.
Changing up the $readable_handles var may help. The IO::Select constructor returns a select object, which accesses the state of all the handles, whether they be Readable, Writeable, or Exceptions. Also the constructor auto adds any handles passed in.
# $readable_handles = Select::IO->new(); # $readable_handles->add($server_sock); $select_object = Select::IO->new($server_sock);
The select::IO object extends the capabilities of the select function to act upon not only one, but stored arrays of, filehandles.
The Example in the IO::Select documentation shows the while argument can actually be the call to read the ready select. Maybe this is something that you have already tried ?
#while(1){ while(@readables = $select_object->can_read){
using the correct equality operator ? this could be a straight forward syntax error - as the equality operator earlier in the code is the double equals symbol ?
# if( $sck eq $sock ){} if( $sck == $sock ){}
This does not look as though it will do what you appear to attempt to be doing. You are trying to store messages to be sent in a hash queue keyed by the ports. But it looks like it will overwrite the message que in the hash, with a random message from the queue. the seed being the number of clients connected.
else { # store message in queue push( @clients , $text[1] ); #--> --^ # further down ... print Dumper(\@clients); $trans = "OK"; $client_data = &send($trans); print "Second send: ".$client_data."\n"; } } # End of if ($text[0] eq "NICK") elsif ($text[0] eq "MSG") { if (length($text[1]) > MAXBYTES) { $trans = "".$error." Please remember that message limit is + ".MAXBYTES.""; $client_data = &send($trans); print "In case of message over ".MAXBYTES." send: ".$clien +t_data."\n"; } else { print "Second receive: ".$text[1]."\n"; print "This is \$sock: ".$sock."\n"; # Get all client(s) socket(s) my @sockets = $readable_handles->can_write(); #my $count = $readable_handles->count(); # store messages in hash ? # for as many sockets that are readable, take the message # with the index of this this number, from the message # queue and replace the current hashed socket queue with # it. # that is, if 5 clients then each client will continue to # recieve the fifth messages from the message queue. # or at least teh log will only record that, depending on # whr STDOUT for ($_ = 0; $_ < @sockets; $_++) { $hash{$sock} = $clients[$_]; } # or $hash{$sock} = $clients[ scalar @{$select_object->can_write()} ]
Maybe this is some kind of buddy inviting incentive, get a freind to sign in and see the next message. idk...
ok I think I can see what you are trying to do now.
I cleaned up the server code a bit, including accessing the select 3 x ref to array in a ref to a array. But I tried to clear the fork out of the client (i am on wins rigth now) but that went wrong. so, so far I have...
Also removed a fair bit of the if else constructs, was hard to see where the flow was going. Hopefully you can see some of the transitions, such as the double quoting interpolations. I think you will particularly like the dereference of the return array from the select call.
#!/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 $error = "ERROR"; my %hash = (); # global variable my ( $client_data , $buf , $sock , $msg , $new_sock , $trans , $reada +ble_handles , $client , $port ); unless (@ARGV == ARGUMENTS) { print "\nPlease only ARGUMENTS input!\n"; print "Correct Syntax: perl $0 IP:PORT (e.g. 127.0.0.1:5000)\n"; exit(); } # User message IP:PORT ( my ( $inputip, $inputport ) = ( $ARGV[0] =~ m/^( # $1 $inputip (?: #non-capturing \d{1,3}\. #1-3 digits followed by stop ){3} # x3 \d{1,3} #last 1-3 digit of ip address ) : # colon (\d+) # $2 $inputport $/x ) ); # endmatch print "::$inputip:-:$inputport:\n"; my $server_sock = IO::Socket::INET->new( LocalAddr => $inputip, LocalPort => $inputport, Proto => 'tcp', Listen => SOMAXCONN, Reuse => 1 ) or die "Could not connect: $!"; print "\n[Server $0 accepting clients at IP: $inputip and PORT: $inp +utport.]\n"; # $readable_handles = IO::Select->new(); my $select_object = IO::Select->new($server_sock); while (1) { my @readables = IO::Select->select($select_object, undef, unde +f, 0) ; foreach $sock ( @{ $readables[0] } ) { # Check if sock is the same with server (e.g. 5000) # if same (new client) accept client socket if ($sock == $server_sock) { $new_sock = $sock->accept() or die sprintf "ERROR (%d)(%s)(%d)(%s)", $!,$!,$^E,$^E; $select_object->add($new_sock); $trans = "Hello version"; print { $new_sock } utf8::encode( $trans ); print "First send: $trans\n"; }else{ # read from socket input $buf = <$sock>; my ($msg , $port) = receive($buf); my @text = split(/ / , $msg , 2); # LIMIT = 2 Only the first t +wo gaps split #print Dumper(@text); if ($text[0] eq "NICK") { $hash{$port} = $text[1]; print Dumper(\%hash); #print Dumper(\@names); $trans = "OK"; print { $sock } utf8::encode( $trans ); print "Second send: $trans\n"; }elsif ($text[0] eq "MSG") { print "Second receive: ".$text[1]."\n"; # Get all client(s) socket(s) #my @names = values %hash; my @sockets = $select_object->can_write(); # possible problem ? # none writeable - only 'select'ed readables writeabl +e ?? # (my $new_readable) = IO::Select->select($select_object, undef, + undef, 0); #print Dumper(\@sockets); # Send the same message to client(s) foreach my $sck (@sockets) { my $final = "$text[0] $hash{$port} $text[1] \n"; utf8::encode($final); print { $sck } $final; print "Third send: $final"; #print STDOUT "The following data send to Client(s): ( +\ ".$buf." \)\n"; } }else{ print "Closing client!\n"; # when the client disconnects delete $hash{$port}; $select_object->remove($sock); close($sock); } } # End of else condition ($sock == $server_sock) } # End of foreach $sock @readables } # End of While (1) print "Terminating Server\n"; close $server_sock; getc(); sub send { utf8::encode( $_[0] ); print { $new_sock } $_[0],"\n"; # chomp ($_[0]); # ? chomp encoded line? #print "The following data send to Clients: (\ ".$_[0]." \)\n"; #$client_sock->send($client_packet,MAXBYTES); return $_[0]; } sub receive { #$new_sock->recv($client_data,MAXBYTES); my $datarecieved = utf8::decode($_[0]); # assign $1 to $shortdata may need correcting. my( $shortdata ) = ( m/^(.{0,20})/ =~ $datarecieved ); my( $phost, $pport ) = ( $new_sock->peerhost(), $new_sock->peerpor +t() ); my $fromhostport = "From host: $phost and port: $pport"; print "This:$shortdata\n$fromhostport\n"; return( $datarecieved, $pport ); #(?) should not get here... utf8::encode (qq{ $error, \n } ); $server_sock->send($error); print "Invalid client: $phost : terminating!\n"; $select_object->remove($sock); close($sock); }
please do post up any questions, and let me know if anything I suggested is of any help, or completely no good. Well, I have not got this one at all, I run this and I am getting binary output onto buffers.
DoC
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re^2: How to retrieve the port number on a Multiple TCP Chat Client Server
by thanos1983 (Parson) on Jul 16, 2014 at 23:26 UTC |