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

I'd like to read from a socket, one byte at a time. At first, I was trying this by autoflushing the socket handle using select(SOCKET); $|++; select(STDOUT); but that didn't seem to work as I wanted. Perhaps that was autoflushing my input to the socket. I've tried looking into 'man 2 setsockopt' and tried setting the RCVBUF = 1 but that only reads 1 bytes of the buffer then quits for the day. Please don't tell me to use IO::Select/Socket as I've been toying with this for nearly a month and have used every module that I could get my hands on and have yet to successfully clear the input buffer (socket -> program) character by character.. an example would be GREATLY appreciated.

-brad.. confused/annoyed.
  • Comment on how do I read from a socket one byte at a time?

Replies are listed 'Best First'.
Re: how do I read from a socket one byte at a time?
by MeowChow (Vicar) on Jan 25, 2001 at 02:49 UTC
    There's really no point in reading one byte at a time. Do you mean sending? If so, check out what the Sockets FAQ has to say on this matter here. Short story: there's no guaranteed way to send data one byte at a time.

    Though, I suspect from the way you've phrased your question, that what you're really want to do is read in as much as you can into a buffer, and then just process the buffer character-by-character.

      actually, I guess I wasn't very clear..
      what I want to do is read from the socket realtime parsing data as its sent not waiting for the "\n" to be able to process that line..

      hrm.. does that make sense? basically what I'm trying to simulate here is a "telnet host; traceroute otherhost" and see the little "*" or the hops REALTIME.. not wait for 3 timeouts on each little "*" to process the line. this isn't exactly what I'm doing I'm interacting with a program that writes unbuffered to a handle, but it appears that somewhere the sockets I'm creating are not allowing me to read from them unbuffered like telnet or ssh sessions would..

      -brad.. looking at Expect.pm
        You would certainly want to employ select() and read() instead of your usual filehandle methods (i.e. '<F>') which block until a linefeed, or the value in $/ if it is set to something else, is received.

        The select() function lets you know when there's something to read, which means you can do something else while you're waiting, such as time-out if nothing happens.

        This program should give you the data as it comes in from a socket:
        #!/usr/bin/perl -w use strict; use IO::Socket; my ($host) = "www.perlmonks.org"; my ($port) = "http(80)"; my ($socket) = new IO::Socket::INET (PeerAddr => $host, PeerPort => $port, Proto => 'tcp'); die "Can't connect to $host:$port\n" unless $socket; my ($rfd) = ''; # Must be initialized by string, # not numeric my ($timeout) = 5.0; # 5s timeout my ($block_size) = 10_000_000; # Buffer "size" at ~10MB $|++; # Unbuffer STDOUT # Send a sample transaction via HTTP $socket->write ("GET / HTTP/1.0\r\nHost: $host\r\n\r\n"); $socket->flush(); # Wait loop to receive content while (1) { # Set the bit-flag for the socket you are waiting on = 1 vec ($rfd, $socket->fileno(), 1) = 1; # Wait for something to happen if (select ($rfd, undef, undef, $timeout) >= 0 && vec($rfd, $socket->fileno(), 1)) { # Something came in! my ($buffer); # Check what it is by calling read() on the socket my ($result) = $socket->read ($buffer, $block_size); # Print out what came in print $buffer; # Drop out of the loop if read() returns a zero # value, indicating EOF. last unless $result; } else { # Timed out on the select(), so bail out. last; } }
        I haven't used IO::Socket much, so this was interesting practice. Normally, I just use Socket, which is a far sight better than the Perl4 method using pack(). Hash-style named parameters are all the rage these days, and I'm not complaining. I really should port all my stuff over as soon as I can.

        Additionally, you can set the timeout parameter of the select() call to be 0 which means that select() will return immediately, without waiting. This is useful if you need to check if some new data has arrived, but have better things to do than wait around for it.
        In that case, use recv() or read(), instead of reading from your socket/filehandle using the <HANDLE> operator. Or, localize $_ to undef.
Re: how do I read from a socket one byte at a time?
by kschwab (Vicar) on Jan 25, 2001 at 18:57 UTC
    If you aren't interested in the gory details of sockets, Net::Telnet might be interesting for you.

    Here's an example that does what I think you want. It doesn't read character by character, but it does print output as it reaches the buffer, rather than waiting for a newline:

    #!/usr/bin/perl # unbuffer STDOUT, so you can watch the # traceroute output "realtime" select STDOUT;$|=1; use Net::Telnet; my($host)="yourhost"; my($trace)="www.cnn.com"; my($username)="username"; my($passwd)="password"; my($t)=Net::Telnet->new(Host => $host); defined($t) or die; $t->login($username, $passwd) or die; $t->print("traceroute $trace") or die; while (defined($data = $t->get())) { print $data; # supposing your prompt is a dollar # sign and that doesn't ever appear in # traceroute output... last if ($data =~ /\$/); } print "\n"; $t->print("exit"); $t->close;
    Good luck...