perlipc bidirectional unix domain socket
am 22.09.2007 15:47:07 von unknownPost removed (X-No-Archive: yes)
Post removed (X-No-Archive: yes)
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
Post removed (X-No-Archive: yes)
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 =
> 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=
> $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
On 22 Sep, 14:47, all mail refused
> 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 wrote:
> On 22 Sep, 14:47, all mail refused
>> 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
Post removed (X-No-Archive: yes)