un be-knoweth has asked for the wisdom of the Perl Monks concerning the following question:

...I'll try to make it short. Forgive my newbism. Since I made chat server allowing multiple clients, I wanted to make a GUI for the client. The GUI is in Tk and the client is expected to put the address and their name in the fields and then launch a connection from a button... this loads a subroutine which I want to use to take the strings from the fields, create a socket, recieve messages constantly and insert it to the beginning of the Listbox. Unfortuneately, when I do this it just freezes because it's only focusing on this routine and no messages are currently sent. here's the code:
use Tk; use IO::Socket::INET; $er1 = "Connection was not yet made..."; $er2 = "No current sockets to close..."; $main = MainWindow->new(); $main->title('Conors Chat Client'); $main->minsize(50, 5); $main->configure(-background=>'black'); $top = $main->Frame(-background => 'black')->pack(-side=>'left', -fill +=>'x'); $options = $main->Frame(-background => 'black')->pack(-side=>'right', +pady=>8, padx=>8); $mess = $top->Frame(-background => 'black')->pack(-side=>'left', pady= +>8, padx => 8); $box = $mess->Listbox(-relief => 'sunken', -width => 50, -height => 5, -setgrid => 'yes', -background=>'Dark + Red'); $scroll = $mess->Scrollbar(-command => ['yview', $box]); $box->configure(-yscrollcommand => ['set', $scroll]); $box->pack(-side => 'left', -fill => 'both', -expand => 'yes'); $scroll->pack(-side => 'left', -fill => 'y'); @hello = ("Welcome to Chat Client V1.0"); foreach (@hello){ $box->insert('end', $_); } $msg1 = $mess->Entry(-width=>8, -background =>'Dark Red')->pack(-side= +>'bottom'); $mess->Button(-text=> 'Send', -background=>'Dark Red', -command=> sub{ +sender($msg1)})->pack(-side=>'bottom'); $options->Label(-text => 'Server Host:', -background => 'Dark Red')->p +ack; $serverip = $options->Entry(-width=>8, -background =>'Dark red')->pack +; $options->Label(-text => 'Name:', -background => 'Dark Red')->pack; $name = $options->Entry(-width=>8, -background => 'Dark red')->pack; $options->Button(-text => 'Start Connection', -background => 'Dark red', -command => sub{connection($serverip, $name)})->pack; $options->Button(-text => 'Stop Connection', -background => 'Dark red' +, -command => sub{endconnect})->pack; $options->Button(-text => 'Exit', -background => 'dark red', -command +=> sub{exit})->pack(-side=> 'bottom'); MainLoop; sub connection { ($serverip, $name) = @_; $servadd = $serverip->get; $name1 = $name->get; @serv = split(/:/, $servadd); print "Server: $serv[0] and port: $serv[1]"; <b> $sock = new IO::Socket::INET->new(PeerAddr=>$serv[0], PeerPort= +>$serv[1],Proto=>'tcp') or die "Can't Connect!!!"; while($sock){ $sock->recv($msg, 100); if($msg ne ''){ $box->insert('active', $msg); } }</b> } sub endconnect { if(defined ($sock)){ close ($sock); } else { $box->insert('active', $er2); } } sub sender { $mess1 = $msg1->get; if(defined ($sock) and msg1 ne ''){ $sock->send("$name1: $mess1"); $box->insert('active', $name1.':'.$mess1); } else { $box->insert('active', $er1); } }
I ask of a solution to have the receiving of messages run in the background

Replies are listed 'Best First'.
Re: Tk and socket question
by graff (Chancellor) on Mar 05, 2004 at 01:59 UTC
    I noticed the part of code that you meant to highlight with "<b> ... </b>" -- of course, that sort of highlighting doesn't work within "code" tags, and monks should be looking for real comment lines within code sections to learn about your intentions/problems with the code...

    Anyway, I think the part your missing might be found in "perldoc Tk::fileevent" -- this allows Tk to continue with its own event loop (for handling user inputs at the GUI), while also watching for activity on an open file handle or socket.

    The basic idea is that when Tk is running the GUI, you don't do " while <INP_HANDLE> { ... }" anywhere in the code, unless your intent is to read the given handle all the way to EOF before handling any other GUI activity. Instead, you open your handle (file or socket), create a handler callback to read from that handle whenever Tk detects available input on the handle, deal with that event, and hand control back to the Tk event loop (until it detects more input on the handle).

    (I haven't used it much myself, so I'm not the best person to offer sample code, but the docs should be clear enough...)

Re: Tk and socket question
by bageler (Hermit) on Mar 05, 2004 at 04:19 UTC
    I have a Tk network client app that has multiple network streams open at once. I used threads to take care of putting things in the background and threadshared variables to let my mainloop know how they're doing.
      Do you have any Tk code that shows how to work with threads? I hear it not easy and I would love to see it work...maybe post it as a snippet as it will be helpful to others wanting to attempt it???
Re: Tk and socket question
by esskar (Deacon) on Mar 05, 2004 at 01:33 UTC
    i sugest to
    1. use strict;
    2. try to build an console based chat before attempting Tk stuff
      1. i usually do, i just wrote that more as the 'beta'.
      2. already done. see code below.
      use strict; use IO::Socket; use IO::Select; my $listen = IO::Socket::INET->new(Proto => 'tcp', LocalPort => 9192, Listen => 1, Reuse => 1) or die $!; my $select = IO::Select->new($listen); my @ready; while(@ready = $select->can_read) { my $socket; for $socket (@ready) { if($socket == $listen) { my $new = $listen->accept; $select->add($new); print $new->fileno . ": connected\n"; } else { my $line=""; $socket->recv($line,80); if($line eq "") { print $socket->fileno . ": disconnected\n"; $select->remove($socket); $socket->close; }; my $socket; for $socket ($select->handles) { next if($socket==$listen); $socket->send($line) or do { print $socket->fileno . ": disconnected\n"; $select->remove($socket); $socket->close; }; } } } }
      god bless select().
        sorry, that^ was me. and esskar- i didn't think i did such a bad job on my tk.
Re: Tk and socket question
by zentara (Cardinal) on Mar 05, 2004 at 14:31 UTC
    When you move from regular Perl to Tk, you have to adjust to the idea of the "event loop" in Tk. You don't want to setup anything to "block" it's flow. Typically you use Tk::after to check something on a time interval,and move on. In your case, the socket is blocking and one solution is to use IO::Select. But Tk has a built in feature to do this called Tk::fileevent.
    How do I poll for input - as in:
    
    while ($msg = <STDIN>) {}
    while ($sock->recv($msg)) {}
    
    Look up "Tk::fileevent":
    
          Tk::fileevent - Execute a callback when a filehandle
          becomes readable or writable
    
    SYNOPSIS
          $widget->fileevent(fileHandle,readable?,callback?)
    
          $widget->fileevent(fileHandle,writable?,callback?)
    
    DESCRIPTION
          This command is used to create file event handlers.  A
          file event handler is a binding between a filehandle and a
          callback, such that the callback is evaluated whenever the
          filehandle becomes readable or writable.  File event han
          dlers are most commonly used to allow data to be received
          from another process on an event-driven basis, so that the
          receiver can continue to interact with the user while
    
    
    

    I'm not really a human, but I play one on earth. flash japh
Re: Tk and socket question
by Grygonos (Chaplain) on Mar 05, 2004 at 14:52 UTC

    I wrote one of these in Java a year ago...mainly because of Java's good (IMHO) thread support. You really will need threads to make this work. Threads for

    • Incoming
    • Outgoing
    • Who's Online (possibly)
    • Any other things you want to persistently monitor.. ie ping ponging the server occasionally to exit gracefully if you have a disconnect
    Your server may need threads for
    • each connection
    • and a listener thread for initiating new connections and logging them
    Keep in mind these are just my ideas and a basic structure to get you going on... if you don't have thread experience you're really going to want to read up on them before continuing on this project.

    Hope my comments were helpful,

    Grygonos