2015-09-22 3 views
5

Я пытаюсь написать основное приложение для сетевого чата в Perl для учебных целей. В настоящее время у меня есть серверная и клиентская программа, которые работают практически так, как я хочу. Несколько клиентов могут подключаться к серверу и отправлять сообщения на него и из него. Тем не менее, я не совсем уверен, как идти о передаче сообщений от одного клиента другому, и я бы по достоинству оценил толчок в правильном направлении. Вот код, который у меня есть до сих пор, мысли?Основное приложение сетевого чата в Perl

Примечание: Это моя первая попытка использовать сеть или использовать Perl для надлежащего проекта, чтобы также было оценено любое другое руководство по его написанию.

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

Это на самом деле довольно хорошо для вашего уровня опыта.Единственное, что я сразу скажу, это то, что вы должны убедиться, что ваш учебный материал обновлен. '-w' в командной строке или строке shebang уже давно вытесняется превосходными' предупреждениями использования'. Вы также должны объявить '$ child' в код сервера внутри' while', непосредственно перед 'if' – Borodin

ответ

1

Один клиент к одному серверу, не так уж сложно - то, что вы делаете с вашим кодом там - эффективно - создает отношения от 1 до 1. Ваш сервер fork работает исключительно с вашим клиентом.

Чтобы получить информацию для распространения (через сервер) между несколькими клиентами, вам придется немного усложниться - потому что у вас есть отдельные процессы, теперь эти процессы должны взаимодействовать друг с другом. Это довольно большой вопрос о том, что существует целый сегмент документации по perl: perlipc.

Это существенно увеличит сложность вашего кода, потому что вы переходите к отношениям «один ко многим» в ваших сообщениях, и все они будут происходить асинхронно.

Связь на основе сокетов - одна из форм межпроцессного взаимодействия (IPC), и вы уже это делаете. Но ваш «getcha» здесь состоит в том, что вы переходите от 1 до 1 комм. До 1 ко многим. Вы должны иметь возможность транслировать, и этот способ связи не поддерживает это особенно хорошо.

То, что я хотел бы предложить взглянуть на IO::Pipe - Я некоторый пример кода здесь: How to extract data from Parallel::ForkManager in perl

Затем использовать IO::Select и can_read асинхронно решить, есть ли какие-либо данные, поступающие в на трубе. Вам, вероятно, понадобится массив труб - по одному на каждого клиента - в противном случае вы можете совпадать с совпадающими материалами.

Например:

(Из IO::Pipe док страница:

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

К сожалению, есть небольшой глюк здесь - ваши трубы будут созданы в процессе запускающим, но это в свою очередь означает, что вам нужно выяснить, как обрабатывать массив труб и проверять, можно ли их читать.

Это означает, что вы не можете больше сидеть в цикле while вокруг приемных сокетов - это блокирует, поэтому у вас будут очереди сообщений ООН пока другой клиент не подключится (что действительно не будет тем, что вы хотите). Поэтому вам также нужно будет снова использовать select, чтобы проверить, есть ли что-то готовое к accept.

+0

Спасибо за ответ, если честно, я все еще очень смущен тем, как это реализовать. Могу ли я по-прежнему делать это только с одним процессом на соединение или мне нужно будет снова вилка? Как я могу передать пакет данных, полученный из одного подключения ко всем открытым соединениям? У меня была идея; скажем, я каждый раз разбудил процесс прослушивания данных, как только один получил некоторые данные, я мог бы использовать список, общий для всех процессов, содержащих все живые соединения (хотя я не уверен, как это сделать, или если это возможно), и просто перебирайте все соединения и отправляйте их так. Мысли? –

+0

Сложнее, чем кажется, потому что совместное использование списков через fork сложно. Тем не менее. – Sobrique

+0

Тот же вывод, к которому я пришел, не мог бы вы немного рассказать о том, как я могу использовать потоки здесь? –

Смежные вопросы