2010-02-04 7 views
8

Sto lavorando su un server TCP con multithreading. Nel thread principale, ascolto su un socket e creo un nuovo thread per le nuove connessioni in entrata. Voglio salvare tutte le connessioni in arrivo in un hash in modo che possa accedervi da un altro thread.Come si salvano i socket in un hash e si passano sopra ad essi da un altro thread?

Dal thread del monitor, non riesco a leggere nessuna connessione appena aggiunta. Sembra che un nuovo hash dei client sia stato creato durante la creazione del thread del monitor.

Come posso mantenere un elenco di tutte le prese e collegarle in loop dal thread del monitor?

codice attuale:

#!/usr/bin/perl 
use strict; 
use IO::Socket; 
use threads; 
use Thread::Queue; 

# init 
my $clients = {}; 
my $queue = Thread::Queue->new; 

# thread that monitors 
threads->create("monitor"); 

# create the listen socket 
my $listenSocket = IO::Socket::INET->new(LocalPort => 12345, 
             Listen => 10, 
             Proto => 'tcp', 
             Reuse => 1); 

# make sure we are bound to the port 
die "Cant't create a listening socket: [email protected]" unless $listenSocket; 

print "Server ready. Waiting for connections on 34567 ... \n"; 

# wait for connections at the accept call 
while (my $connection = $listenSocket->accept) { 
    # set client socket to non blocking 
    my $nonblocking = 1; 
    ioctl($connection, 0x8004667e, \\$nonblocking); 

    # autoflush 
    $connection->autoflush(1); 

    # debug 
    print "Accepted new connection\n"; 

    # add to list 
    $clients->{time()} = $connection; 

    # start new thread and listen on the socket 
    threads->create("readData", $connection); 
} 

sub readData { 
    # socket parameter 
    my ($client) = @_; 

    # read client 
    while (<$client>) { 
     # remove newline 
     chomp $_; 

    # add to queue 
     $queue->enqueue($_); 
    } 

    close $client; 
} 

sub monitor { 
    # endless loop 
    while (1) { 

     # loop while there is something in the queue 
     while ($queue->pending) { 

      # get data from a queue 
      my $data = $queue->dequeue; 

      # loop all sockets 
      while (my ($key, $value) = each(%$clients)) { 

       # send to socket 
       print $value "$data\n"; 

      } 
     } 

     # wait 0,25 seconds 
     select(undef, undef, undef, 0.25); 
    } 
} 

close $listenSocket; 
+0

Suggerimento, forse utile per te, forse no: mai visto un modulo chiamato 'IO :: Multiplex'? – fennec

risposta

8

Hai bisogno di condividere $clients via share da threads::shared:

my $clients = &share({}); 

La sintassi vecchia maniera è a causa di un problema documentato con i prototipi di Perl. Se hai at least Perl 5.8.9, usa il più bello

my $clients = shared_clone({}); 

invece.

Si desidera inoltre proteggere $clients con un blocco, ad es.,

my $clients_lock : shared; 
{ 
    lock $clients_lock; 
    $clients->{time()} = fileno $connection; 
} 

Infine, perché IO::Socket::INET casi sono typeglob Perl, non si possono condividere, così invece di aggiungere i loro descrittori di socket (da fileno) per $clients e poi fdopen presa quando necessario con

open my $fh, ">&=", $sockdesc or warn ... 

Il programma qui sotto ripete i dati in entrata alle altre prese collegate:

#!/usr/bin/perl 

use strict; 
use IO::Socket; 
use threads; 
use threads::shared; 
use Thread::Queue; 

# init 
my $clients = &share({}); 
my $clients_lock : shared; 

my $queue = Thread::Queue->new; 

# thread that monitors 
threads->create("monitor"); 

# create the listen socket 
my $port = 12345; 
my $listenSocket = IO::Socket::INET->new(
    LocalPort => $port, 
    Listen  => 10, 
    Proto  => 'tcp', 
    Reuse  => 1 
); 

# make sure we are bound to the port 
die "Can't create a listening socket: [email protected]" unless $listenSocket; 

print "Server ready. Waiting for connections on $port ... \n"; 

# wait for connections at the accept call 
while (my $connection = $listenSocket->accept) { 
    # set client socket to non blocking 
    my $nonblocking = 1; 
    ioctl($connection, 0x8004667e, \\$nonblocking); 

    # autoflush 
    $connection->autoflush(1); 

    # debug 
    print "Accepted new connection\n"; 

    # add to list 
    { 
    lock $clients_lock; 
    $clients->{time()} = fileno $connection; 
    } 

    # start new thread and listen on the socket 
    threads->create("readData", $connection); 
} 

sub readData { 
    # socket parameter 
    my ($client) = @_; 

    # read client 
    while (<$client>) { 
    chomp; 
    $queue->enqueue($_); 
    } 

    close $client; 
} 

sub monitor { 
    # endless loop 
    while (1) { 
    # loop while there is something in the queue 
    while ($queue->pending) { 
     # get data from a queue 
     my $data = $queue->dequeue; 

     # loop all sockets 
     { 
     lock $clients_lock; 
     while (my ($key, $value) = each(%$clients)) { 
      # send to socket 
      if (open my $fh, ">&=", $value) { 
      print $fh "$data\n"; 
      } 
      else { 
      warn "$0: fdopen $value: $!"; 
      } 
     } 
     } 
    } 

    # wait 0,25 seconds 
    select(undef, undef, undef, 0.25); 
    } 
} 

close $listenSocket; 
+1

+1 Avevo appena capito le stesse cose. –

+0

Grazie! Funziona come un fascino ora :) – Dieterve

+0

@Dieterve Siete i benvenuti! –

1

Non ce l'ho troppa esperienza utilizzando thread in Perl, ma penso che si desidera solo per condividere la vostra lista di clienti:

 
    use threads::shared ; 
    my $clients : shared = {}; 


Aggiornamento:

Perl si lamenta:

my $hash : shared = {}; 

ma sembra essere ok con:

my $hash = {}; 
share($hash); 

Inoltre, questo codice:

my $hash = { key1 => "value1" }; 
share($hash); 

sembra cancellare la tabella hash, ma

my $hash = {}; 
share($hash); 
$hash->{key1} = "value1"; 

funziona come mi aspetterei.

+0

Questo non funziona, la seconda riga restituisce un errore "Valore non valido per scalare condiviso su test.pl riga 9." – Dieterve

Problemi correlati