http://www.perlmonks.org?node_id=1058085


in reply to How to introduce threading in socket communication

Hi simonz,

The following is a very basic example. It's important to note that the perl/Tk code isolated from any thread functionality, since perl/Tk itself is NOT thread-safe.

Your client and server code have been made into their own threads, essentially unmodified. The only changes were to chomp the incoming buffer to the server from the client, and to push it onto the shared server_msgs array so it can be visible to the Tk program.

#!/usr/bin/perl -w # # Shows a very basic example of perl thread usage, simultaneous with # perl/Tk code. Note that perl/Tk (which is NOT thread-safe) *must* # be kept isolated from any thread functionality. # # References: # http://www.perlmonks.org/?node_id=1056724 # # 2013-10-13 golux ############### ## Libraries ## ############### use strict; use warnings; use threads; use threads::shared; use Tk; use Tk::ROText; ################## ## User-defined ## ################## my $tk_title = 'perl/Tk with threads (basic example) - 2013-10-13 gol +ux'; ############## ## Globals ## ############## my @server_msgs : shared; share(@server_msgs); ################## ## Main Program ## ################## my $thread1 = threads->create(\&server_thread)->detach; my $thread2 = threads->create(\&client_thread)->detach; create_tk_gui(); ################# ## Subroutines ## ################# #===================# ## Tk Main Program ## #===================# sub create_tk_gui { my $mw = new Tk::MainWindow(-title => $tk_title); my $top = frame($mw, 'B'); my $f1 = frame($top, 'x'); my $f2 = frame($top, 'B'); my $b1 = tk_pack($f1->Button(-text => 'Exit (^X)', -bg => 'cyan') +, '>'); my $txt = tk_pack($f2->ROText(-bg => '#ffefb2'), '^B'); my $h = { 'mw' => $mw, 'text' => $txt }; $b1->configure(-command => sub { exit }); $txt->insert("1.0", "[Server Messages]\n"); $mw->bind('<Control-x>' => sub { $b1->invoke }); $mw->repeat(250 => [ \&gui_loop, $h ]); $mw->MainLoop; } sub frame { my ($w, $pack, @args) = @_; my $frame = $w->Frame(@args); return tk_pack($frame, $pack); } sub tk_pack { my ($w, $pack) = @_; my $h_fill = { qw[ n none x x y y b both ] }; my $h_side = { qw[ < left > right ^ top v bottom ] }; $pack ||= '^n'; my $side = ($pack =~ s/^([<>^v])//)? $1: '^'; $pack ||= 'n'; my $fill = lc $pack; my $exp = ($fill eq $pack)? 0: 1; $fill = $h_fill->{$fill}; $side = $h_side->{$side}; $w->pack(-expand => $exp, -fill => $fill, -side => $side); return $w; } sub gui_loop { my ($h) = @_; if (0 == @server_msgs) { return; } my $txt = $h->{'text'}; while (@server_msgs) { my $msg = shift @server_msgs; $txt->insert('end', "$msg\n"); } } #=================# ## Server Thread ## #=================# sub server_thread { print "[Server]\n"; use IO::Socket; use IO::Select; use strict; my $server = IO::Socket::INET::->new(Proto => 'tcp', LocalPort => 55555, Listen => 1, Reuse => 1 ) or die "Server can't start: + $!"; my $readable_handles = new IO::Select(); $readable_handles->add($server); my $buf; while (1) { # select() blocks until a socket is ready to be read or written my ($new_readable) = IO::Select->select($readable_handles, undef, undef, 0); # If it comes here, there is at least one handle # to read from or write to. For the moment, worry only about # the read side. foreach my $sock (@$new_readable) { print "Inside foreach $sock \n"; if ($sock == $server) { my $new_sock = $sock->accept(); # Add it to the list, and go back to select because the # new socket may not be readable yet. $readable_handles->add($new_sock); } #- server part else { #print STDERR "Reading...\n"; # It is an ordinary client socket, ready for reading. $buf = <$sock>; if ($buf) { chomp $buf; #- print the buffer # print "Read $buf\n"; push @server_msgs, "Read $buf"; # .... Do stuff with $buf } else { # Client closed socket. We do the same here, and remove # it from the readable_handles list $readable_handles->remove($sock); close($sock); } } } } } #=================# ## Client Thread ## #=================# sub client_thread { print "[Client]\n"; use IO::Socket; my $client = IO::Socket::INET::->new( Proto => 'tcp', PeerAddr => 'localhost', PeerPort => 55555 ) or die "Client can't connect: $!"; my @msgs = 1 .. 100; for (@msgs) { print $client "$_\n"; sleep 1; } }
say  substr+lc crypt(qw $i3 SI$),4,5

Replies are listed 'Best First'.
Re^2: How to introduce threading in socket communication
by BrowserUk (Patriarch) on Oct 13, 2013 at 17:11 UTC

    # sub server_thread { print "[Server]\n"; use IO::Socket; use IO::Select; use strict; my $server = IO::Socket::INET::->new(Proto => 'tcp', LocalPort => 55555, Listen => 1, Reuse => 1 ) or die "Server can't start +: $!"; my $readable_handles = new IO::Select(); ... # It is an ordinary client socket, ready for reading. $buf = <$sock>; ...

    Using buffered IO reads with an IO::Select server is fundamentally flawed. If any one client sends a packet that doesn't contain the right delimiter -- whether through programmer error; or because the user aborts the client mid-transmission; or because tcp decides to fragment the packet at an inappropriate point -- then your server will hang indefinitely.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.