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

Well I have decided to abandon using POE to make a chat server since it is not ready for threads. In that case I am going back to IO::Sockets in order to achieve building a chat server/client. So this message has to do with two separate (but together) programs.
First the client program:
#!/usr/bin/perl use warnings; use strict; use IO::Socket; use Gtk2 -init; use Glib qw/TRUE FALSE/; my $main_window = Gtk2::Window->new("toplevel"); $main_window->signal_connect(delete_event => sub{Gtk2->main_quit}); my $buffer = Gtk2::TextBuffer->new; my $table = Gtk2::Table->new(3, 2, FALSE); my $label = Gtk2::Label->new("Chat Client Test"); my $textview = Gtk2::TextView->new_with_buffer($buffer); my $swindow = Gtk2::ScrolledWindow->new( undef, undef); $swindow->set_size_request (100, 150); my $button = Gtk2::Button->new("Send"); my $entry = Gtk2::Entry->new(); $textview->set_cursor_visible (FALSE); $textview->set_editable(FALSE); $buffer->create_mark ('end', $buffer->get_end_iter, FALSE); $buffer->signal_connect (insert_text => sub {$textview->scroll_to_mark + ($buffer->get_mark ('end'),0.0, TRUE, 0, 0.5)}); $swindow->set_policy( 'automatic', 'automatic'); $swindow->set_shadow_type( 'etched-out'); $swindow->add( $textview); $table->attach_defaults($label, 0, 1, 0, 1); $table->attach_defaults($swindow, 0, 2, 1, 2); $table->attach_defaults($entry, 0, 1, 2, 3); $table->attach_defaults($button, 1, 2, 2, 3); $main_window->add($table); $main_window->show_all(); $button->signal_connect("clicked" =>sub { #get the contents of the entry my $msg_send = $entry->get_text; #clear the entry $entry->set_text(""); #grab focus again for the next round of talks $entry->grab_focus; #if there was bogus input, ignore it! if ($msg_send !~ m/^\s*$/){ #open up a UDP client connection my $SEND_PORT = 5152; my $server_host = "Deadpickle-hobo"; my $sock = IO::Socket::INET->new(Proto => 'udp', PeerP +ort => $SEND_PORT, PeerAddr => $server_host) or die "Creating socke +t: $!\n"; #send the message out on the socket $sock->send($msg_send."\n") or die "send: $!"; #update the screen locally #&update_buffer($buffer,$msg_send,TRUE); } }); Gtk2->main;
This program is just a beginner I'm not sure if this is how I want it to work quit yet. The error I get with this program is:
*** unhandled exception in callback:
*** Creating socket: Invalid argument
*** ignoring at client.pl line 62.
But right now I would really like to get a chat server going.Webmonkey has a good tutorial on how to create a chat server but their code link has an error. So I guess I'm asking for help on creating a chat server and the client I'll work on later. The server I have so far I dont think works very well for a chat server:
#!/usr/bin/perl -w use strict; use IO::Socket; use Gtk2 -init; use Glib qw/TRUE FALSE/; use Gtk2::Helper; my($sock,$MAXLEN, $LISTEN_PORT, $SEND_PORT,$tag_send,$tag_receive,$im +g_big,$img_send,$img_rec); $MAXLEN = 1024; $LISTEN_PORT = 5151; my $tview; #set up a udp server waiting for incomming messages $sock = IO::Socket::INET->new(LocalPort => $LISTEN_PORT, Proto => 'ud +p') or die "socket: $@"; #add a Gtk2::Helper watch on any incomming connections Gtk2::Helper->add_watch ( fileno $sock, 'in',$sock); print "Awaiting UDP messages on port $LISTEN_PORT\n";
Any ideas on how to get this to work correctly?

Replies are listed 'Best First'.
Re: Building a chat server and client
by quester (Vicar) on Jan 09, 2008 at 07:48 UTC
    I can get your IO::Socket::INET->new(...) call to work on my Linux box, but only if $server_host is a valid IP address or host name, like "0.0.0.0" or "www.yahoo.com". Check if you have "Deadpickle-hobo" in your /etc/hosts file (or \windows\system32\drivers\etc\hosts if you're on Windows). If the host name in $server_host is set up correctly you should be able to ping it like this:
    ping Deadpickle_hobo
Re: Building a chat server and client
by zentara (Cardinal) on Jan 09, 2008 at 14:32 UTC
    You make a few big errors in your client. 1. a different socket number from server. 2. No gtk2 main loop in client so it exits immediately. 3. No callback for the Gtk2->Helper to watch the socket. 4. Need to return 1 from Helper to keep it in play.

    Try this, it works on linux.

    #server #!/usr/bin/perl use warnings; use strict; use IO::Socket; use Gtk2 -init; use Glib qw/TRUE FALSE/; my $main_window = Gtk2::Window->new("toplevel"); $main_window->signal_connect(delete_event => sub{Gtk2->main_quit}); my $buffer = Gtk2::TextBuffer->new; my $table = Gtk2::Table->new(3, 2, FALSE); my $label = Gtk2::Label->new("Chat Client Test"); my $textview = Gtk2::TextView->new_with_buffer($buffer); my $swindow = Gtk2::ScrolledWindow->new( undef, undef); $swindow->set_size_request (100, 150); my $button = Gtk2::Button->new("Send"); my $entry = Gtk2::Entry->new(); $textview->set_cursor_visible (FALSE); $textview->set_editable(FALSE); $buffer->create_mark ('end', $buffer->get_end_iter, FALSE); $buffer->signal_connect (insert_text => sub {$textview->scroll_to_mark + ($buffer->get_mark ('end'),0.0, TRUE, 0, 0.5)}); $swindow->set_policy( 'automatic', 'automatic'); $swindow->set_shadow_type( 'etched-out'); $swindow->add( $textview); $table->attach_defaults($label, 0, 1, 0, 1); $table->attach_defaults($swindow, 0, 2, 1, 2); $table->attach_defaults($entry, 0, 1, 2, 3); $table->attach_defaults($button, 1, 2, 2, 3); $main_window->add($table); $main_window->show_all(); $button->signal_connect("clicked" =>sub { #get the contents of the entry my $msg_send = $entry->get_text; #clear the entry $entry->set_text(""); #grab focus again for the next round of talks $entry->grab_focus; #if there was bogus input, ignore it! if ($msg_send !~ m/^\s*$/){ #open up a UDP client connection my $SEND_PORT = 5152; my $server_host = "localhost"; my $sock = IO::Socket::INET->new(Proto => 'udp', PeerPort => +$SEND_PORT, PeerAddr => $server_host) or die "Creating socket: $!\n" +; #send the message out on the socket $sock->send($msg_send."\n") or die "send: $!"; #update the screen locally #&update_buffer($buffer,$msg_send,TRUE); } }); Gtk2->main;
    and the client
    #!/usr/bin/perl -w use strict; use IO::Socket; use Gtk2 -init; use Glib qw/TRUE FALSE/; use Gtk2::Helper; my($sock,$MAXLEN, $LISTEN_PORT, $SEND_PORT, $tag, $tag_send, $tag_receive,$img_big,$img_send,$img_rec); $MAXLEN = 1024; $LISTEN_PORT = 5152; my $tview; #set up a udp server waiting for incomming messages $sock = IO::Socket::INET->new(LocalPort => $LISTEN_PORT, Proto => 'ud +p') or die "socket: $@"; #add a Gtk2::Helper watch on any incomming connections print "Awaiting UDP messages on port $LISTEN_PORT\n"; $tag = Gtk2::Helper->add_watch ( fileno($sock), 'in', sub { if (eof($sock)) { Gtk2::Helper->remove_watch ($tag); close($sock); } else { my $line = <$sock>; #$buffer->insert($buffer->get_end_iter,$line); print "$line"; } return 1; }); Gtk2->main;

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Thanks for the replies, they are very helpful! But I'm still lost.
      The server should listen for incoming messages. When it receives messages it should then broadcast them to all the connected users. Also, the server is in charge of the connected users. This entails checking for duplicate usernames and informing the connected users of new and leaving users. The thing is that I don't know where to start in this whole diboggle. I'm not familiar with sockets and writing server scripts.

      For the client it should connect to the server and share its user name. The users connection should be rejected if there is a similar username or should be forced to have a different name. with that taken care of, it should work just like a chat client does. you type and then the message is sent to the server where it is broadcast to all servers even the owner.

      I have been trying to build the scripts from scratch and so far I have a client:

      #server #!/usr/bin/perl use warnings; use strict; use IO::Socket; my $SERVER = "Deadpickle-hobo"; my $SEND_PORT = 5152; my $line; my $send_socket = IO::Socket::INET->new( PeerAddr => $SERVER, PeerPort => $SEND_PORT, Proto => 'tcp', Reuse => 1 ) or die "socket: $@"; #ask for the user name and send it to the server #in order to verifiy that its unique print "Username:"; my $user = <>; $send_socket->send($user); $send_socket->recv($line, 80); print $line; $send_socket->recv($line, 80); print $line;
      and a server:
      #!/usr/bin/perl -w use strict; use IO::Socket; use IO::Select; #variables my $LISTEN_PORT = 5152; my $user; my @users; my $fh; my @ready; #open a new socket my $listening_socket = IO::Socket::INET->new( LocalPort => $LISTEN_PORT, Proto => 'tcp', Listen => 1, Reuse => 1 ) or die "socket: $@"; my $select = new IO::Select($listening_socket); #wait for a client to connect to the server print "Awaiting TCP messages on port $LISTEN_PORT\n" if $listening_soc +ket; while(@ready = $select->can_read) { foreach $fh (@ready) { if($fh == $listening_socket) { #Create a new socket and get the user ID my $new = $listening_socket->accept; $new->recv($user, 80); #Check the newly connected user for copies if (my $new_user = grep($user, @users)) { } else { $select->add($new); push(@users, $user); $new->send("Hello\n"); $new->send("@users"); } } else { # Process socket # Maybe we have finished with the socket $select->remove($fh); $fh->close; } } } print "END\n";
      I am requesting for help on implementing the ideas I mentioned above.
        I'm not in the mood right now for writing your code, but study the following tcp example, it shows the basic idea, You need to push the socket filehandle and an associated name into an array or hash, then loop thru them to send to all socket filehandles. I don't believe you can just send to an array of filehandles, like you show with $new->send("@users");

        Also you need to listen to more than 1, for a multiecho operation.

        #!/usr/bin/perl #server use IO::Socket; use IO::Select; my @sockets; my $machine_addr = 'localhost'; $main_sock = new IO::Socket::INET(LocalAddr=>$machine_addr, LocalPort=>1200, Proto=>'tcp', Listen=>3, Reuse=>1, ); die "Could not connect: $!" unless $main_sock; print "Starting Server\n"; $readable_handles = new IO::Select(); $readable_handles->add($main_sock); while (1) { ($new_readable) = IO::Select->select($readable_handles, undef, undef +, 0); foreach $sock (@$new_readable) { if ($sock == $main_sock) { $new_sock = $sock->accept(); $readable_handles->add($new_sock); } else { $buf = <$sock>; if ($buf) { print "$buf\n"; my @sockets = $readable_handles->can_write(); #print $sock "You sent $buf\n"; foreach my $sck(@sockets){print $sck "$buf\n";} } else { $readable_handles->remove($sock); close($sock); } } } } print "Terminating Server\n"; close $main_sock; getc();
        #!/usr/bin/perl -w # interactive client use strict; use IO::Socket; my ( $host, $port, $kidpid, $handle, $line ); ( $host, $port ) = ('localhost',1200); my $name = shift || ''; if($name eq ''){print "What's your name?\n"} chomp ($name = <>); # create a tcp connection to the specified host and port $handle = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port ) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # split the program into two processes, identical twins 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 = <$handle> ) ) { print STDOUT $line; } kill( "TERM", $kidpid ); # send SIGTERM to child } # the else{} block runs only in the child process else { # copy standard input to the socket while ( defined( $line = <STDIN> ) ) { print $handle "$name->$line"; } }

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Building a chat server and client
by ambrus (Abbot) on Sep 27, 2013 at 14:42 UTC