http://www.perlmonks.org?node_id=807990

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to read from a socket file created by vlc's rc interface here. As I am doing it via cgi, I need to chop the otherwise interactive stream of comand-response-command-etc. into consumable chunks.
Some of my code:
# establish connection my $socket = IO::Socket::UNIX->new( Peer => '/path/to/socket.file.sock', ) # issue command and read answer print $socket "$some_command\n"; $html = "<html>". sockread($socket) "</html>"; sub sockread { my $socket = shift; my $content; while (my $line=<$socket>){ $content .= $line; last if $line =~ /\[ End of /i; # vlc's rc is +a bit inconsistent, some end like this last if $line =~ / returned 0 /i; # others like +this } return $content; }
Is there a more elegant way to detect when the socket stops to spit out data? (As some outputs tend to lack my "stop words")

Replies are listed 'Best First'.
Re: Chunked unix socket file reading - how?
by snoopy (Curate) on Nov 18, 2009 at 19:53 UTC
    IO::Select will let you do a timed-wait for input.

    This will avoid your reader hanging indefinitely, if for what ever reason you don't receive expected transmissions in full.

    Haven't tested it here but the usage should be something like:

    #!/usr/bin/perl use common::sense; use IO::Socket; use IO::Select; # establish connection my $socket = IO::Socket::UNIX->new( Peer => '/path/to/socket.file.sock', ); my $sel = IO::Select->new(); $sel->add($socket); # issue command and read answer my $some_command = 'foo'; print $socket "$some_command\n"; my $html = "<html>". sockread($sel,$socket). "</html>"; sub sockread { my $sel = shift; my $socket = shift; my $content; my $TimeOut = 0.5; #sec to wait while ($sel->can_read($TimeOut) && (my $line=<$socket>)){ $content .= $line; last if $line =~ /\[ End of /i; # vlc's rc is a bit in +consistent, some end like this last if $line =~ / returned 0 /i; # others like this } return $content; }
    Update: Corrected $TimeOut from ms to seconds.
      @snoopy: timeout on can_read() is in seconds, not ms! Had more success with can_read(1), e.g.
        Quite right. IO::Select, passes the timeout straight through to the select function, which expects the units to be seconds, and can accept fractional quantities (e.g. 0.5).

        I've updated the code above.

      Further testing shows: it's actually not the read that blocks. It is the my $socket = IO::Socket::UNIX->new( Peer => $sockfile ); that never returns!

      Is there a mechanism like the "Reuse" on file sockets as well? Is it possible some kind of lock lies on the file?

      Even more testing shows: when the sock gets stale like this, even socat isn't able to connect to it (it doesn't complain but vlc doesn't react via the socket anymore)
      Is this a vlc problem then?
      You were too fast ;)

      I had that feeling that I hadn't looked into IO::Select enough... (Currently testing...)
      Mmmh. Everything looked as if your code is *the solution*. Then came testing....

      I still get these looong reads that completely timeout/block my cgi. Some connects work, a few times and then, nirvana. The cgi immediately returns when I kill the vlc process on the server side.

      Somehow I wonder if it's really my read that doesn't return or some hickup on the vlc side.
      • Any tips on how I could make my socket reading/connection more robust, so that it cares less about stale sockets? (socat never seems to care..)
      • Should I issue the "logout" command vlc provides inside the rc session on each socket connect-command-read-"logout" cycle via my cgi (this cgi will never see much hits) So that vlc knows better: "this client is connecting, asking one thing, then disconnects"? (I know this is lsightly off-perl-topic)
Re: Chunked unix socket file reading - how?
by almut (Canon) on Nov 18, 2009 at 19:44 UTC
    ...when the socket stops to spit out data

    What exactly does that mean? Is the socket being closed on the other side, or does it simply no longer send data; are there always complete lines (terminated by \n), etc...?

      from the side of vlc the socket stays "connected", while my cgi connects-sendsCommand-reads-terminates on each http request cycle.
      The problem is with the -read- part, where my cgi can't detect so well if the issued command was answered correctly or not. On the interactive e.g. socat/netcat shell I (human) can easily see when a command is fully answered by the socket "stopping to spit out data". Anyway, my script can't be that smart.

      vlc sends ~5-30 lines of answer data, each terminated by \n. That's why I try to detect common "last lines" of output - but as said, it doesn't work on every output.
      Is there a way to limit my read to, let say, max 100 lines?
      Or a common algorithm to set timeouts on read-while loops?