Kevin Melkowski
Kevin Melkowski

Reputation: 483

Programming a chat room in perl, I'm having issues with the client?

I'm following this guide explaining how to do a server using IO::Async but I'm having issues with my client code. I have it where I send first then receive. This makes me press enter on each client before receiving any data. I figured I'd have to listen till I wanted to type something but I'm not really sure how. Below is my current client code.

use IO::Socket::INET;

# auto-flush on socket
$| = 1;

# create a connecting socket
my $socket = new IO::Socket::INET (
    PeerHost => 'localhost',
    PeerPort => '12345',
    Proto => 'tcp',
);
die "cannot connect to the server $!\n" unless $socket;
print "My chat room client.  Version One.\n";

while (1) {
    my $data = <STDIN>;
    $socket->send($data);
    my $response = "";
    $socket->recv($response, 1024);
    print ">$response";
    last if (index($data, "logout") == 0);
}

$socket->close();

Upvotes: 1

Views: 574

Answers (3)

Gabs00
Gabs00

Reputation: 1877

I actually had this problem myself a few weeks ago when trying to make a client/server chat for fun.

Put it off until now.

The answer to your problem of having to hit enter to receive data, is that you need to use threads. But even if you use threads, if you do $socket->recv(my $data, 1024) you won't be able to write anything on the command line.

This isn't using your code, but here is my solution after banging my head against a wall for the last 24hrs. I wanted to add this as an answer, because though the question is out there on stackoverflow, none of the answers seemed to show how to use IO::Select.

Here is the server.pl script, it does not use threading:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;
use IO::Select;

$| = 1;
my $serv = IO::Socket::INET->new(
    LocalAddr => '0.0.0.0',
    LocalPort => '5000',
    Reuse => 1,
    Listen => 1,
);

$serv or die "$!";

print 'server up...';
my $sel = IO::Select->new($serv); #initializing IO::Select with an IO::Handle / Socket

print "\nAwaiting Connections\n";

#can_read ( [ TIMEOUT ] )
#can_write ( [ TIMEOUT ] )
#add ( HANDLES )
#http://perldoc.perl.org/IO/Select.html

while(1){
    if(my @ready = $sel->can_read(0)){ #polls the IO::Select object for IO::Handles / Sockets that can be read from
        while(my $sock = shift(@ready)){
            if($sock == $serv){
                my $client = $sock->accept();
                my $paddr = $client->peeraddr();
                my $pport = $client->peerport();

                print "New connection from $paddr on $pport";

                $sel->add($client); #Adds new IO::Handle /Socket to IO::Select, so that it can be polled 
                            #for read/writability with can_read and can_write
            }
            else{
                $sock->recv(my $data, 1024) or die "$!";
                if($data){                  
                    for my $clients ($sel->can_write(0)){
                        if($clients == $serv){next}
                        print $clients $data;
                    }

                }
            }
        }
    }
}

And the client.pl, which uses threads:

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket::INET;
use threads;
use IO::Select;
$| = 1;

my $sock = IO::Socket::INET->new("localhost:5000");
$sock or die "$!";
my $sel = IO::Select->new($sock);


print "Connected to Socket ". $sock->peeraddr().":" . $sock->peerport() . "\n";

#This creates a thread that will be used to take info from STDIN and send it out
#through the socket.

threads->create(
    sub {
        while(1){
            my $line = <>;
            chomp($line);
            for my $out (my @ready = $sel->can_write(0)){
                print $out $line;
            }
        }   
    }
);

while(1){
    if(my @ready = $sel->can_read(0)){
        for my $sock(@ready){
            $sock->recv(my $data, 1024) or die $!;
            print "$data\n" if $data;
        }
    }

}

There is one other problem that arises though, when the client receives data and prints it to the console, your cursor goes to a new line, leaving behind any characters you had typed.

Hope this helps and answers your question.

Upvotes: 2

LeoNerd
LeoNerd

Reputation: 8532

For a simple "just send from STDIN, receive to STDOUT" client, you could use any of telnet, nc or socat. These will be simple enough to use for testing.

$ telnet localhost 12345

$ nc localhost 12345

$ socat stdio tcp:localhost:12345

If you actually want to write something in Perl, because you want to use it as an initial base to start a better client from, you probably want to base that on IO::Async. You could then use the netcat-like example here. That will give you a client that looks-and-feels a lot like a simple netcat.

Upvotes: 1

sgauria
sgauria

Reputation: 458

I am guessing you need to set the MSG_DONTWAIT flag on your recv call, and print the response only if it is non-null.

$socket->recv($response, 1024, MSG_DONTWAIT);
print ">$response" if ($response ne "");

Upvotes: 0

Related Questions