in reply to Re^2: Building a chat server and client
in thread Building a chat server and client

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

Replies are listed 'Best First'.
Re^4: Building a chat server and client
by deadpickle (Pilgrim) on Jan 14, 2008 at 22:42 UTC
    I'm finally getting it (I'm a little slow). Here is a Gtk2 script that loads the client GUI and asks for a username. But I have ran into a problem, I receive this error:
    *** unhandled exception in callback:
    *** Undefined subroutine &main:: called at chat-client.pl line 54.
    *** ignoring at chat-client.pl line 54.
    This error seems to stem from the wait_for_msg timer that I have added and the error references the Gtk2->main loop. When the timer is commented out the program works fine. What can I do.
    #!/usr/bin/perl use warnings; use strict; use Gtk2 -init; use Glib qw/TRUE FALSE/; use IO::Socket::INET; use Tie::RefHash; use IO::Select; #global variables my $buffer; my $host = "Deadpickle-hobo"; my $port = 6666; my $conn_stat = 'idle'; my %inbuffer = (); my %outbuffer = (); my %ready = (); my $select; #my $timer_waiting = Glib::Timeout->add(100, wait_for_msg()); #the main chat widget my $main_window = Gtk2::Window->new("toplevel"); $main_window->signal_connect(delete_event => sub {Gtk2->main_quit;}); $main_window->set_default_size(250, 200); my $table = Gtk2::Table->new(4, 2, FALSE); $buffer = Gtk2::TextBuffer->new; my $button = Gtk2::Button->new("Send"); my $entry = Gtk2::Entry->new(); my $label = Gtk2::Label->new("Chat Client Test"); my $textview = Gtk2::TextView->new_with_buffer($buffer); $textview->set_cursor_visible (FALSE); my $swindow = Gtk2::ScrolledWindow->new( undef, undef); $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, 3); $table->attach_defaults($entry, 0, 1, 3, 4); $table->attach_defaults($button, 1, 2, 3, 4); $main_window->add($table); $main_window->show_all(); #run the login dialog dialog($buffer); Gtk2->main; #-------------------Login Dialog------------------- sub dialog{ my $buffer = shift; my $dialog_window = Gtk2::Window->new('toplevel'); $dialog_window->signal_connect(delete_event => sub {Gtk2->main_quit} +); my $dialog_table = Gtk2::Table->new(2, 2, FALSE); my $dialog_label1 = Gtk2::Label->new('Chat Login:'); my $dialog_label2 = Gtk2::Label->new('User:'); my $dialog_label3 = Gtk2::Label->new('Host:'); my $chat_user = Gtk2::Entry->new(); $chat_user->set_text(''); my $server_user = Gtk2::Entry->new(); $server_user->set_text('uas'); my $host = Gtk2::Entry->new(); $host->set_text('updraft.unl.edu'); my $dialog_button1 = Gtk2::Button->new('Connect'); $dialog_table->attach_defaults($dialog_label1, 0, 1, 0, 1); $dialog_table->attach_defaults($chat_user, 1, 2, 0, 1); $dialog_table->attach_defaults($dialog_button1, 1, 2, 1, 2); $dialog_button1->signal_connect("clicked" => sub {my $tag = $chat_us +er->get_text; $dialog_window->destroy; $buffer->insert(($buffer->get_ +end_iter), "Username: $tag...\n"); connect_server()}); $dialog_window->add($dialog_table); $dialog_window->show_all; return 1; } #------------------Connect to server--------------------- #establishes connection to the server sub connect_server{ if ($conn_stat ne 'connected') { $buffer->insert(($buffer->get_end_iter), "Connecting to Server $ho +st:$port...\n"); my $conn = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $p +ort, Proto => 'tcp') or popup_err(1); if ($conn) { %inbuffer = (); %outbuffer = (); %ready = (); tie %ready, 'Tie::RefHash'; nonblock($conn); $select = IO::Select->new($conn); $conn_stat = 'connected'; $buffer->insert(($buffer->get_end_iter), "Connected!\n"); #my $timer_waiting = Glib::Timeout->add(100, wait_for_msg()); } } } #-------------------Error popup------------------- # pops up an error message sub popup_err{ my ($error_code) = @_; my $error; if ($error_code == 1) {$error = "Cannot create Socket!"}; $buffer->insert(($buffer->get_end_iter), "$error\n"); my $error_dialog = Gtk2::MessageDialog->new($main_window, 'destroy-w +ith-parent', 'error', 'ok', "$error"); $error_dialog->run; $error_dialog->destroy; } #-------------------blocking------------------- # nonblock($socket) puts socket into nonblocking mode sub nonblock { my $socket = shift; $socket->blocking(0); } #-------------------Message Waiting------------------- # Wait for incoming messages from the server relayed from clients sub wait_for_msg { if ($conn_stat eq 'connected') { my ($list_size, $msg); my $server; my $rv; my $data; # check for new information on the connections we have # anything to read or accept? foreach $server ($select->can_read(1)) { # read data $data = ''; $rv = $server->recv($data, 'POSIX::BUFSIZ', 0); unless (defined($rv) && length $data) { # This would be the end of file, so close the client delete $inbuffer{$server}; delete $outbuffer{$server}; delete $ready{$server}; $select->remove($server); close $server; next; } $inbuffer{$server} .= $data; # test whether the data in the buffer or the data we # just read means there is a complete request waiting # to be fulfilled. If there is, set $ready{$client} # to the requests waiting to be fulfilled. while ($inbuffer{$server} =~ s/(.*\n)//) { push( @{$ready{$server}}, $1 ); } } # Any complete requests to process? foreach $server (keys %ready) { handle($server); } } }

    UPDATE: I accidentally clipped the use references.
      I tried your script, and it needed
      use Gtk2 qw (init'); use IO::Socket;
      to get it to run. Then,
      my $conn = IO::Socket::INET->new(PeerAddr => $host, PeerPort => $por +t, Proto => 'tcp') or popup_err(1);
      caused the error to popup. Remove the popup_err(1) and it seems to start.

      I didn't try to debug it any further, because the tie %ready looked kind of complex to me.


      I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      I have ran into a wall. When I run the client, Login and then try to type and send a message I get a print to closed filehandle GEN0 error. This error is in the send_msg_all print line and seems to involve the socket variable $conn. I can't seem to figure out how the filehandle is close. This error seems to be born from only the client script. I hope someone here can help me out.

      WARNING: the scripts is long. I couldnt slim it down since the login is required by the server. But I do hope that the flow chart will help weed out the confusion.