Interactive Client with IO::Socket

- Interactive Client with IO::Socket

## Interactive Client with IO::Socket

Well, that's all fine if you want to send one command and get one answer, but what about setting up something fully interactive, somewhat like the way telnet works? That way you can type a line, get the answer, type a line, get the answer, etc.

This client is more complicated than the two we've done so far, but if you're on a system that supports the powerful `fork` call, the solution isn't that rough. Once you've made the connection to whatever service you'd like to chat with, call `fork` to clone your process. Each of these two identical process has a very simple job to do: the parent copies everything from the socket to standard output, while the child simultaneously copies everything from standard input to the socket. To accomplish the same thing using just one process would be much harder, because it's easier to code two processes to do one thing than it is to code one process to do two things. (This keep-it-simple principle a cornerstones of the Unix philosophy, and good software engineering as well, which is probably why it's spread to other systems.)

Here's the code:

        #!/usr/bin/perl -w
        use strict;
        use IO::Socket;
        my ($host, $port, $kidpid, $handle, $line);

        unless (@ARGV == 2) { die "usage: $0 host port" }
        ($host, $port) = @ARGV;

        # create a tcp connection to the specified host and port
        $handle = IO::Socket::INET->new(Proto     => "tcp",
                                        PeerAddr  => $host,
                                        PeerPort  => $port)
               or die "can't connect to port $port on $host: $!";

        $handle->autoflush(1);              # so output gets there right away
        print STDERR "[Connected to $host:$port]\n";

        # split the program into two processes, identical twins
        die "can't fork: $!" unless defined($kidpid = fork());

        # the if{} block runs only in the parent process
        if ($kidpid) {
            # copy the socket to standard output
            while (defined ($line = <$handle>)) {
                print STDOUT $line;
            }
            kill("TERM", $kidpid);                  # send SIGTERM to child
        }
        # the else{} block runs only in the child process
        else {
            # copy standard input to the socket
            while (defined ($line = <STDIN>)) {
                print $handle $line;
            }
        }

The `kill` function in the parent's `if` block is there to send a signal to our child process (current running in the `else` block) as soon as the remote server has closed its end of the connection.

If the remote server sends data a byte at time, and you need that data immediately without waiting for a newline (which might not happen), you may wish to replace the `while` loop in the parent with the following:

        my $byte;
        while (sysread($handle, $byte, 1) == 1) {
            print STDOUT $byte;
        }

Making a system call for each byte you want to read is not very efficient (to put it mildly) but is the simplest to explain and works reasonably well.

- Back to A Webget Client in the perlipc manpage

- Forward to TCP Servers with IO::Socket in the perlipc manpage

- Up to the perlipc manpage

Interactive Client with IO::Socket