perlipc bidirectional unix domain socket

Discussion in 'Perl Misc' started by all mail refused, Sep 22, 2007.

  1. I've been trying to put bidirectional traffic through a
    local domain (aka Unix domain) socket by adapting code
    from "man perlipc" as seen at
    http://perl.active-venture.com/pod/perlipc-sockets.html
    where the local stuff comes near the end of the page.

    The original code works unidirectional - i.e. the client
    reads something from the server but after making what look
    to me like suitable changes I've been unable to get the server
    to obtain the request from the client. I see the original
    server opens STDIN from <&Client but doesn't use it.

    I've also simplified it by taking the multithread stuff
    out of the server. Tested so far on Linux but aiming to
    go on multiple Unix versions.

    Can someone show me where I'm falling down?

    Client
    ======

    #!/usr/bin/perl -w
    use Socket;
    use strict;
    my ($rendezvous, $line);

    $rendezvous = shift || 'catsock';
    socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die();
    connect(SOCK, sockaddr_un($rendezvous)) or die();
    printf(SOCK "Request something\n");
    shutdown(SOCK,1); # finished writing
    $line = <SOCK>;
    print $line;
    exit;


    Server
    ======

    #!/usr/bin/perl -Tw
    use strict;
    use sigtrap;
    use Socket;
    use Carp;

    BEGIN {%ENV=('PATH'=>'/usr/ucb:/bin')}

    sub spawn; # forward declaration
    sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }

    my $NAME = 'catsock';
    my $uaddr = sockaddr_un($NAME);
    my $proto = getprotobyname('tcp');

    socket(Server,PF_UNIX,SOCK_STREAM,0) or die();
    unlink($NAME);
    bind(Server, $uaddr) or die();
    listen(Server,SOMAXCONN) or die();
    logmsg "server started on $NAME";

    accept(Client,Server) or die("accept $!");
    # Is Client now bidirectional?

    logmsg "connection on $NAME";
    spawn sub {
    my $input=shift;
    printf("(%s): Reply=XXXX\n", $input);
    };

    sub spawn {
    my $coderef = shift;
    my $input;

    unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
    confess "usage: spawn CODEREF";
    }

    close(STDIN);
    open(STDIN, "<&Client") or die("open STDIN $!");
    # lsof here shows we do have fd0 dup from one of the previous fds
    open(STDOUT, ">&Client") or die("open STDOUT $!");

    $input=<STDIN>;
    $input="sample" unless (defined($input));
    chomp($input);
    printf(STDERR "Now executing coderef(%s)...\n", $input);
    exit &$coderef($input);
    }


    --
    Elvis Notargiacomo master AT barefaced DOT cheek
    http://www.notatla.org.uk/goen/
    all mail refused, Sep 22, 2007
    #1
    1. Advertising

  2. all mail refused wrote:
    > I've been trying to put bidirectional traffic through a
    > local domain (aka Unix domain) socket by adapting code
    > from "man perlipc" as seen at
    > http://perl.active-venture.com/pod/perlipc-sockets.html
    > where the local stuff comes near the end of the page.
    >
    > The original code works unidirectional - i.e. the client
    > reads something from the server but after making what look
    > to me like suitable changes I've been unable to get the server
    > to obtain the request from the client. I see the original
    > server opens STDIN from <&Client but doesn't use it.
    >
    > I've also simplified it by taking the multithread stuff
    > out of the server. Tested so far on Linux but aiming to
    > go on multiple Unix versions.


    Your sample code seems to work for me without modification:

    shhhh$ perl -T server.pl & sleep 2 && perl client.pl
    [1] 18983
    Name "main::Client" used only once: possible typo at server.pl line 22.
    server.pl 18983: server started on catsock at Sat Sep 22 16:10:52 2007
    server.pl 18983: connection on catsock at Sat Sep 22 16:10:54 2007
    Now executing coderef(sample)...
    (sample): Reply=XXXX
    [1]+ Exit 1 perl -T server.pl

    > Can someone show me where I'm falling down?


    Can you tell us what specific behavior you are observing, and how it
    differs from what you expected?

    Regards,
    Mik
    Mike Pomraning, Sep 22, 2007
    #2
    1. Advertising

  3. On 2007-09-22, Mike Pomraning <> wrote:


    > all mail refused wrote:
    >> I've been trying to put bidirectional traffic through a
    >> local domain (aka Unix domain) socket by adapting code
    >> from "man perlipc" as seen at
    >> http://perl.active-venture.com/pod/perlipc-sockets.html
    >> where the local stuff comes near the end of the page.


    > Your sample code seems to work for me without modification:
    >
    > Can you tell us what specific behavior you are observing, and how it
    > differs from what you expected?


    I'm getting the same behaviour as you, but that is not what I call working.

    The client is supposed to send "Request something\n" through the
    socket where the server should read it on STDIN into the variable
    $input. Instead $input is undefined after the read and so replaced
    by "sample".

    "(Request something): Reply=XXXX" was the hoped-for output from the client.

    --
    Elvis Notargiacomo master AT barefaced DOT cheek
    http://www.notatla.org.uk/goen/
    all mail refused, Sep 23, 2007
    #3
  4. all mail refused

    Ben Morrow Guest

    Quoth all mail refused <>:
    > I've been trying to put bidirectional traffic through a
    > local domain (aka Unix domain) socket by adapting code
    > from "man perlipc" as seen at
    > http://perl.active-venture.com/pod/perlipc-sockets.html
    > where the local stuff comes near the end of the page.
    >
    > The original code works unidirectional - i.e. the client
    > reads something from the server but after making what look
    > to me like suitable changes I've been unable to get the server
    > to obtain the request from the client. I see the original
    > server opens STDIN from <&Client but doesn't use it.
    >
    > I've also simplified it by taking the multithread stuff
    > out of the server. Tested so far on Linux but aiming to
    > go on multiple Unix versions.
    >
    > Can someone show me where I'm falling down?


    (I realise this is mostly copied from the perldoc: the style of the code
    is rather out-of-date, and they should probably be updated.)

    > Client
    > ======
    >
    > #!/usr/bin/perl -w


    use warnings;

    is better than -w.

    > use Socket;


    It's usually easier to use IO::Socket than the raw socket calls.

    > use strict;
    > my ($rendezvous, $line);


    It's better not to declare variables until you need them. Perl is not C
    :).

    > $rendezvous = shift || 'catsock';
    > socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die();
    > connect(SOCK, sockaddr_un($rendezvous)) or die();


    Don't use global filehandles. If you *really* want to stick to using
    socket() directly, you can use

    socket(my $SOCK, PF_UNIX, SOCK_STREAM, 0) or ...;

    but it would probably be easier to use

    my $SOCK = IO::Socket::UNIX->new($rendevous) or ...;

    which will deal with socket/connect/sockaddr_un for you.

    You should always give a useful error message when you die, including
    the appropriate system error, which for socket functions

    > printf(SOCK "Request something\n");


    Don't use printf when you can use print. In fact, I never use printf at
    all in Perl: on the rare occasion I actually want to printf something, I
    tend to write it as

    print sprintf ...;

    Also, while I don't usually have a problem with parens around function
    arguments (though I avoid them myself), when the first arg is a
    filehandle for print/f the lack of a comma is a little too weird... it
    would be all to easy to put the comma in by mistake and write something
    perfectly valid but quite different. If you're really attached to the
    parens I might even recommend switching to full method syntax:

    $SOCK->print("Request something\n");

    though this requires a

    use IO::Handle;

    before it will work on ordinary filehandles. Hmmm, this is very much a
    matter of style, and a rare case in which Perl's syntax is less than
    ideal, so feel free to ignore this paragraph... :)

    > shutdown(SOCK,1); # finished writing


    It's clearer to use the named constants:

    shutdown($SOCK, SHUT_WR);

    > $line = <SOCK>;
    > print $line;
    > exit;


    There's no need to exit. 'Falling off the end' is a perfectly valid way
    to successfully terminate a Perl program.

    > Server
    > ======
    >
    > #!/usr/bin/perl -Tw
    > use strict;
    > use sigtrap;
    > use Socket;
    > use Carp;
    >
    > BEGIN {%ENV=('PATH'=>'/usr/ucb:/bin')}
    >
    > sub spawn; # forward declaration
    > sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }
    >
    > my $NAME = 'catsock';
    > my $uaddr = sockaddr_un($NAME);
    > my $proto = getprotobyname('tcp');
    >
    > socket(Server,PF_UNIX,SOCK_STREAM,0) or die();
    > unlink($NAME);
    > bind(Server, $uaddr) or die();
    > listen(Server,SOMAXCONN) or die();


    Again, it's probably easier to use

    unlink($NAME);
    my $Server = IO::Socket::UNIX->new(
    Local => $NAME,
    Listen => SOMAXCONN,
    );

    SOCK_STREAM is the default for IO::Socket::UNIX.

    > logmsg "server started on $NAME";
    >
    > accept(Client,Server) or die("accept $!");


    my $Client = $Server->accept
    or die "accept: $!";

    > # Is Client now bidirectional?


    Yes, it is.

    > logmsg "connection on $NAME";
    > spawn sub {
    > my $input=shift;
    > printf("(%s): Reply=XXXX\n", $input);


    No need for printf even here:

    print("($input): Reply=XXXX\n");

    > };
    >
    > sub spawn {
    > my $coderef = shift;
    > my $input;
    >
    > unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE') {
    > confess "usage: spawn CODEREF";
    > }
    >
    > close(STDIN);
    > open(STDIN, "<&Client") or die("open STDIN $!");


    This is a really bad idea. The example in the perldoc was forking a new
    process dedicated to handling one connection, so attaching the std
    handles of the new process only to the client socket was useful. You're
    not doing that (yet?), so just print to and read from the client socket
    directly.

    You're passing the client socket from the main program to this sub as a
    global variable. This is ad for all the usual reasons globals are bad:
    they all apply just as much to filehandles. Use properly scoped
    variables for filehandles, and pass the client socket into spawn as a
    parameter. You will probably also need to pass it into the coderef;
    unless it is created within the scope of $Client above, in which case
    things Just Work (technically, this feature of Perl is called 'lexical
    closure', and only works with anonymous subs).

    Note that if you switch to using a variable for Client, but still want
    to dup it onto STDIN, you have to use the three-arg form of open to do
    so:

    open(STDIN, '<&', $Client) or ...;

    > # lsof here shows we do have fd0 dup from one of the previous fds
    > open(STDOUT, ">&Client") or die("open STDOUT $!");
    >
    > $input=<STDIN>;
    > $input="sample" unless (defined($input));
    > chomp($input);
    > printf(STDERR "Now executing coderef(%s)...\n", $input);
    > exit &$coderef($input);


    I'm not sure what you meant by this, but this will pass exit() the
    return value of the printf that was the last statement in the coderef;
    probably not what you want, as it will usually be 1.

    IMO, 'don't call subs with &' applies to subrefs as well: I would write
    the call as

    $coderef->($input)

    Ben
    Ben Morrow, Sep 23, 2007
    #4
  5. On 22 Sep, 14:47, all mail refused <> wrote:
    > socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die();
    > connect(SOCK, sockaddr_un($rendezvous)) or die();
    > printf(SOCK "Request something\n");
    > shutdown(SOCK,1); # finished writing


    I may be off-base here but ISTR that (at least in some older versions
    of Perl) you had to explicitly set SOCK to be autoflushed (or
    explicitly flush the buffers) when using the low-level socket
    operations rather than IO::Socket.
    Brian McCauley, Sep 23, 2007
    #5
  6. Brian McCauley wrote:
    > On 22 Sep, 14:47, all mail refused <> wrote:
    >> socket(SOCK, PF_UNIX, SOCK_STREAM, 0) or die();
    >> connect(SOCK, sockaddr_un($rendezvous)) or die();
    >> printf(SOCK "Request something\n");
    >> shutdown(SOCK,1); # finished writing

    >
    > I may be off-base here but ISTR that (at least in some older versions
    > of Perl) you had to explicitly set SOCK to be autoflushed (or
    > explicitly flush the buffers) when using the low-level socket
    > operations rather than IO::Socket.


    That's right. Given the OP's clarification of problem, autoflush is the
    issue -- the client doesn't write until after it's read from the server,
    and, indeed, the write fails anyway since shutdown has already been
    called, leaving the server with EOF.

    Something like this:

    ...
    connect(SOCK, sockaddr_un($rendezvous)) or die();
    $| = 1, select $_ for select SOCK;
    printf(SOCK "Request something\n");
    ...

    does the trick. ``perldoc -q flush'' has other $! idioms, and of course
    IO::Socket objects offer ->autoflush().

    -Mike
    Mike Pomraning, Sep 23, 2007
    #6
  7. On 2007-09-23, Mike Pomraning <> wrote:

    >> I may be off-base here but ISTR that (at least in some older versions
    >> of Perl) you had to explicitly set SOCK to be autoflushed (or

    >
    > That's right. Given the OP's clarification of problem, autoflush is the
    > issue -- the client doesn't write until after it's read from the server,
    > and, indeed, the write fails anyway since shutdown has already been
    > called, leaving the server with EOF.
    >
    > Something like this:
    > ...
    > connect(SOCK, sockaddr_un($rendezvous)) or die();
    > $| = 1, select $_ for select SOCK;
    > printf(SOCK "Request something\n");
    > ...
    >
    > does the trick. ``perldoc -q flush'' has other $! idioms, and of course
    > IO::Socket objects offer ->autoflush().


    Thanks a lot everybody. I'd imagined that shutdown() forced flushing of
    the stream up to that point but obviously not.

    --
    Elvis Notargiacomo master AT barefaced DOT cheek
    http://www.notatla.org.uk/goen/
    all mail refused, Sep 23, 2007
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. J Rice
    Replies:
    6
    Views:
    717
    J Rice
    Mar 11, 2006
  2. Manfred Balik
    Replies:
    12
    Views:
    6,596
    Marc Guardiani
    Sep 10, 2006
  3. Jaap Karssenberg

    conflict between man perlipc and man perlfunc !?

    Jaap Karssenberg, Jan 9, 2004, in forum: Perl Misc
    Replies:
    0
    Views:
    155
    Jaap Karssenberg
    Jan 9, 2004
  4. Peter Valdemar Morch

    Signals interrupt accept() - bug in perldoc perlipc?

    Peter Valdemar Morch, Jun 22, 2004, in forum: Perl Misc
    Replies:
    0
    Views:
    131
    Peter Valdemar Morch
    Jun 22, 2004
  5. Nitzan Shaked

    perlipc doc / another race (?)

    Nitzan Shaked, Jun 27, 2005, in forum: Perl Misc
    Replies:
    1
    Views:
    97
Loading...

Share This Page