2015-09-22 9 views
5

Sto provando a scrivere un'app di chat di rete di base in Perl per scopi di apprendimento. Al momento ho un server e un programma client che funzionano quasi come voglio. Più client possono connettersi al server e inviare messaggi da e verso di esso. Tuttavia, non sono davvero sicuro di come mandare i messaggi da un client a un altro e apprezzerei una spinta nella giusta direzione qui. Ecco il codice che ho finora, pensieri?App di chat di rete di base in Perl

Nota: questo è il mio primo tentativo in assoluto di utilizzare il networking o l'utilizzo di Perl per un progetto adeguato in modo che anche altre indicazioni su come è stato scritto siano apprezzate.

chat_server.pl

#!/usr/bin/perl -w 
# chat_server.pl 
use strict; 
use IO::Socket::INET; 

my $port = shift or die "Port required!\n"; 
my $socket = IO::Socket::INET->new(
     LocalPort => $port, 
     Proto  => 'tcp', 
     Listen  => SOMAXCONN 
    ) or die "Can't create socket: $!!\n"; 
my $child; 

print "Listening for clients on $port...\n"; 
REQUEST: 
while(my $client = $socket->accept) { 
    my $addr = gethostbyaddr($client->peeraddr, AF_INET); 
    my $port = $client->peerport; 

    if($child = fork) { 
     print "New connection from $addr:$port\n"; 
     close $client; 
     next REQUEST; 
    } die "fork failed!\n" unless defined $child; 

    while (<$client>) { 
     print "[$addr:$port] says: $_"; 
     print $client "[$addr:$port] says: $_"; 
    } 
} 
close $socket; 

chat_client.pl

#!/usr/bin/perl -w 
# chat_client.pl 
use strict; 
use IO::Socket::INET; 

my $port   = shift or die "No port\n"; 
my $server   = shift or die "No server\n"; 
my $client_socket = IO::Socket::INET->new(
     PeerPort => $port, 
     PeerAddr => $server, 
     Proto  => 'tcp' 
    ) or die "Can't create send socket: $!!\n"; 

my $child; 

if($child = fork) { 
    while(1) { 
     sleep(1); 
     print scalar <$client_socket>; 
    } 
} 

die "fork failed!\n" unless defined $child; 
print "Connected to $server:$port!\n"; 

do { 
    print "> "; 
    print $client_socket $_ if defined $_; 
} while(<STDIN>); 

print "Closing connection"; 
close $client_socket; 
+2

In realtà è abbastanza buono per il tuo livello di esperienza.L'unica cosa che dirò subito è che dovresti assicurarti che il tuo materiale didattico sia aggiornato. '-w' sulla riga di comando o la riga shebang è stata sostituita da" avvisi di uso "superiori. Dovresti anche dichiarare '$ child' nel codice del server all'interno di' while', immediatamente prima del 'if' – Borodin

risposta

1

un singolo client a un singolo server non è troppo difficile - quello che stai facendo con il vostro codice di lì è - in effetti - la creazione di una relazione 1 a 1. Il tuo server fork parla esclusivamente al tuo cliente.

Per ottenere informazioni da propagare (tramite il server) tra più client, sarà necessario ottenere un po 'più complicato: poiché si dispone di processi separati, questi processi ora devono comunicare tra loro. Questa è una domanda abbastanza grande che c'è un intero segmento della documentazione perl a riguardo: perlipc.

Questo in realtà aumenterà la complessità del codice in modo sostanziale, perché ci si sta spostando verso una relazione 1-a-molti sulle comunicazioni e si verificheranno tutte in modo asincrono.

La comunicazione basata su socket è una forma di comunicazione tra processi (IPC) e lo state già facendo. Ma il tuo "pasticcio" qui è che ti stai spostando da 1 a 1 comunicazioni a 1 a molte comunicazioni. Devi essere in grado di trasmettere, e questa modalità di comunicazione non lo supporta particolarmente bene.

Quello che vorrei suggerire è guardare IO::Pipe - ho qualche esempio di codice qui: How to extract data from Parallel::ForkManager in perl

Quindi utilizzare IO::Select e can_read di decidere in modo asincrono se ci sono tutti i dati in arrivo sul tubo. Probabilmente avrai bisogno di una serie di pipe, una per client, altrimenti potresti sovrapporre roba concorrente.

Esempio:

(Da IO::Pipe Pagina doc:

my $pipe = IO::Pipe->new(); 
if($pid = fork()) { # Parent 
     $pipe->reader(); 
     while(<$pipe>) { 
     ... 
     } 
    } 
    elsif(defined $pid) { # Child 
     $pipe->writer(); 
     print $pipe ... 
    } 

Purtroppo c'è un leggero Gotcha qui - le tubature saranno creati dal processo di biforcazione, ma che a sua volta significa che è necessario

Ciò significa che non puoi più sedere su un socket while accettando più socket - che blocca, quindi avresti i messaggi in coda un fino a quando un altro client si connette (che in realtà non sarà quello che vuoi). Quindi dovrai anche usare di nuovo select per verificare se c'è qualcosa pronto per accept prima.

+0

Grazie per la risposta, ad essere onesti, sono ancora estremamente confuso su come implementarlo. Posso fare ancora questo con un solo processo per connessione o dovrei ricominciare da capo? Come posso passare un pacchetto di dati ricevuto da una connessione a tutte le connessioni aperte? Comunque ho avuto un'idea; Diciamo che ogni processo biforcato ha ascoltato i dati, una volta ricevuti alcuni dati, potrei usare una lista condivisa tra tutti i processi che contengono tutte le connessioni viventi (anche se non sono sicuro di come farlo, o se è persino possibile), e solo scorrere tutte le connessioni e inviarlo in questo modo. Pensieri? –

+0

Più difficile di quanto suoni perché condividere gli elenchi tramite fork è difficile. I fili possono però. – Sobrique

+0

Stessa conclusione, sono riuscito a elaborare un po 'su come utilizzare i thread qui? –