Что не так с этим примером IO::Socket::UNIX?

Я пытаюсь реализовать простой клиент / сервер эхо через сокет Unix. (Моя конечная цель - обмен данными JSON, но этот пример для простоты). Я понятия не имею, почему клиентский процесс исчезает в черной дыре, когда он пытается печатать в сокет во второй раз.

server.pl:

use IO::Socket::UNIX;

my $socket_path = '/tmp/mysocket';
unlink $socket_path if -e $socket_path;

my $socket = IO::Socket::UNIX->new(
    Local  => $socket_path,
    Type   => SOCK_STREAM,
    Listen => SOMAXCONN,
);

die "Can't create socket: $!" unless $socket;

while (1) {
    next unless my $connection = $socket->accept;
    chomp( my $line = <$connection> );
    print $connection "$line\n";
}

client.pl:

use IO::Socket::UNIX;

my $socket = IO::Socket::UNIX->new(
    Type => SOCK_STREAM,
    Peer => '/tmp/mysocket',
);

die "Can't create socket: $!" unless $socket;

my $line;

print $socket "one\n";
chomp( $line = <$socket> );
say $line;

print $socket "two\n";
chomp( $line = <$socket> );
say $line;

say "three";

Ожидаемый результат:

> ./client.pl
> one
> two
> three

Фактический вывод:

> ./client.pl
> one

2 ответа

Решение

Вы положили $socket->accept позвоните в свой while петля. После того, как ваш сервер установит соединение и получит некоторый ввод от клиента, следующая вещь, которую он хочет сделать, это установить новое соединение.

Переместить accept позвонить за пределы while петля

my $connection = $socket->accept;
$connection->autoflush(1);
while (my $line = <$connection> ) {
    chomp($line);
    print $connection "$line\n";
}

или, если вы хотите принять более одного подключения,

while (1) {
    next unless my $connection = $socket->accept;
    $connection->autoflush(1);
    while (my $line = <$connection>) {
        chomp($line);
        print $connection "$line\n";
    }
}

Ваше текущее решение также, вероятно, будет "страдать от буферизации", поэтому и сервер, и клиент должны установить autoflush(1) на их обработчиках сокетов.

Теперь для обработки одновременных соединений сервер обычно вызывает fork после получения соединения и обработки этого соединения в дочернем процессе.

while (1) {
    my $connection = $socket->accept;
    if (fork() == 0) {
        $connection->autoflush(1);
        while (my $line = <$connection>) {
            chomp($line);
            print $connection "$line\n";
        }
        close $connection;
        exit;
    }
}

Полный двунаправленный пример для тех, кто ищет:

Сервер:

#! /usr/bin/perl -w

use strict;

use IO::Socket::UNIX qw( SOCK_STREAM SOMAXCONN );

my $SOCK_PATH = '/tmp/test.sock';
unlink($SOCK_PATH) if -e $SOCK_PATH;

my $server = IO::Socket::UNIX->new(
   Type => SOCK_STREAM(),
    Local => $SOCK_PATH,
   Listen => SOMAXCONN,
)
   or die("Can't create server socket: $!\n");

while (1) {
    my $connection = $server->accept;
    if (fork() == 0) {
        print "** New connection received **\n";
        $connection->autoflush(1);
        my $count = 1;
        while (my $line = <$connection>) {
            if ($line){
                chomp($line);
                $connection->print($count . ' -> ' . $line . "\n"); # Sent response back to client, \n terminates
                print "Received and replied to $count '$line'\n";
                $count++;
            }
        }
        close $connection;
        exit;
    }
}

Клиент:

#!/usr/bin/perl -w

use strict;

use IO::Socket::UNIX qw( SOCK_STREAM );

my $SOCK_PATH = '/tmp/test.sock';

my $client = IO::Socket::UNIX->new(
   Type => SOCK_STREAM(),
   Peer => $SOCK_PATH
)
   or die("Can't connect to server: $!\n");

$client->autoflush(1);


## Listen for replies
if (fork() == 0) {
    while (my $line = <$client>) {
        if ($line){
            chomp($line);
            print("Recv: '" . $line . "'\n");
        }
    }
}

## Send something
for my $itm ('Alpha','Beta','Gamma','Delta'){
    print("Send: " . $itm . "\n");
    print($client $itm . "\n") or warn("Can't send: $!\n"); # send to server, \n terminates
}

print "** Client Finished **\n";
Другие вопросы по тегам