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

Hey Guys, I am working with a script which needs to handle multiple tcp clients. I have learned that this can be solved by using IO::Select. I have tested my server script with nc command in Linux and it is working ok. My issue is that i need to take input from user. But my client doesn't receive any information from the server till i have pressed enter on the client. My question is shouldn't $read_handler->can_read() get a value everytime there is incoming data from server? If a value is sent by server then client should be receiving it immediately? P.S. i am new to Perl and socket programming. Any kind of help would be greatly appreciated.
use IO::Socket::INET; use IO::Select; # auto-flush on socket $| = 1; my $server_ip; my $server_port; #my $r,$w; @ARGV == 2 or die "IP and Portnumber needed"; ($server_ip,$server_port)=@ARGV; # create a connecting socket my $socket = new IO::Socket::INET ( PeerHost => $server_ip, PeerPort => $server_port, Proto => 'tcp', ); die "cannot connect to the server $!\n" unless $socket; my $read_handler = IO::Select->new(); $read_handler->add($socket); my $send_buffer; while(1) { @sockets_ready =$read_handler->can_read(); foreach my $read(@sockets_ready) { my $buffer; $buffer = <$socket>; print "$buffer\n"; } @write_ready=$read_handler->can_write(); foreach my $message(@write_ready) { my $send_buffer=<STDIN>; $message->send($send_buffer); } }
This is my server code in case.
#!/usr/bin/perl -w use strict; use warnings; use IO::Socket; use IO::Select; # auto-flush on socket $| = 1; my $server_ip; my $server_port; @ARGV == 2 or die "IP and Portnumber needed"; ($server_ip,$server_port)=@ARGV; # creating a listening socket my $tcp_socket = new IO::Socket::INET( LocalHost => $server_ip, LocalPort => $server_port, Proto => 'tcp', Listen => 5, Reuse => 1 ); die "cannot create socket $!\n" unless $tcp_socket; print "server waiting for client connection on port $server_port\n"; #Create readhandlers with select my $read_select = IO::Select->new(); $read_select->add($tcp_socket); #version send to server my $Version ="Hello <VERSION>\n"; #hash for client my %clients; while(1) { my $new_readable; my $nick; ($new_readable)=IO::Select->select($read_select,undef,undef,0); foreach my $read(@$new_readable) { if($read==$tcp_socket) { my $new_connection = $read->accept(); $new_connection->send($Version); $read_select->add($new_connection); } else { my $buf; my $msg; $buf=<$read>; if($buf) { my @sockets = $read_select->can_write(); #returns a array + of handl$ foreach my $sck(@sockets) { $sck->send("$buf\n"); } } else { $read_select->remove($read); } } } } $tcp_socket->close();

Replies are listed 'Best First'.
Re: Select Tcp Client
by tybalt89 (Monsignor) on Oct 12, 2019 at 21:21 UTC

    Just tweaked a little, try this.

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11107384 use warnings; use IO::Socket; use IO::Select; $| = 1; # auto-flush on socket @ARGV == 2 or die "IP and Portnumber needed"; my ($server_ip,$server_port)=@ARGV; # create a connecting socket my $socket = new IO::Socket::INET ( PeerHost => $server_ip, PeerPort => $server_port, Proto => 'tcp', ); die "cannot connect to the server $!\n" unless $socket; my $read_handler = IO::Select->new(); $read_handler->add($socket, *STDIN); my $send_buffer; while(1) { my @sockets_ready =$read_handler->can_read(); foreach my $read (@sockets_ready) { my $buffer; if( sysread $read, $buffer, 1024 ) { if( $read eq $socket ) { print $buffer; } else { print $socket $buffer; } } else { die "socket closed"; } } }
      Hello, Thank you for your reply. Yes this works fine aswell. I was able to solve the problem by fork (). I see in your example that STDIN is one of the handlers. How can you set a limit on data send to server via stdin?
Re: Select Tcp Client
by NERDVANA (Priest) on Oct 13, 2019 at 02:32 UTC
    Tybalt has the right fix, but I think some additional commentary might help.

    Socket programming can be bit messy. When you read, sometimes you get a partial message, and sometimes you get more than one message. You code will need to read a chunk of data, then find out how many whole messages you got, then process each of them, then hold onto any leftover fragment to be combined with the next read. This needs tracked per-connection. When you ask perl to read a line with <FILEHANDLE> perl will keep reading until it sees an end-of-line. If you have a blocking file handle, this stalls your whole program. But, if you change to non-blocking file handles, perl will think it reached end-of-file and return the fragment even if it isn't a whole line. The second one is a useful behavior, but sysread() is generally a better approach, since it keeps you closer to the native Unix interface (and you need most of that to write a sockets program correctly).

    While doing this is a good learning exercise, and might be sort of a required first step to fully understand sockets, for "getting things done" I recommend an event-driven framework like AnyEvent or IO::Async ecosystem. (Check out AnyEvent:Handle ) These frameworks take care of lots of socket details for you and let you focus on the interesting parts of the program.

      Hei Nedvana, Thank you for your reply. I was able to solve the issue by fork()
Re: Select Tcp Client
by tybalt89 (Monsignor) on Oct 13, 2019 at 10:00 UTC

    BTW, your client is hanging at the <STDIN> because your $socket is always "write ready".

      Yes. <stdin> was blocking all incoming traffic :)
Re: Select Tcp Client
by Anonymous Monk on Oct 13, 2019 at 09:28 UTC
    But my client doesn't receive any information from the server till i have pressed enter on the client.
    This might also be a problem on the client, not only on the server: the client (nc) might be buffering the input, allowing the user to edit the line, until it reads a \n. Only then the input is sent. You can verify that using strace and/or Wireshark.