perlio-socket

What is wrong with this IO::Socket::UNIX example?


I am trying to implement a simple echo client/server, over a Unix socket. (My ultimate goal is to exchange JSON data, but this example is for simplicity). I have no idea why the client process disappears into a black hole when it tries to print to the socket the second time.

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";

Expected output:

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

Actual output:

> ./client.pl
> one

Solution

  • You put the $socket->accept call inside your while loop. After your server establishes a connection and receives some input from the client, the next thing it wants to do is establish a new connection.

    Move the accept call outside the while loop

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

    or, if you do want to accept more than one connection,

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

    Your current solution will also likely be "suffering from buffering", so both the server and the client should set autoflush(1) on their socket handlers.

    Now to handle simultaneous connections, the server would usually call fork after getting a connection, and handling that connection in a child process.

    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;
        }
    }