Object-Oriented Perl
by Simon Cozens
|
Pages: 1, 2
Using it
We've seen a simple use of OO techniques by using
Mail::Header. Let's now look at a slightly more
involved program, to solidify our knowledge. This is a very
simple system information server for a Unix machine. (Don't be put
off -- these programs will work on non-Unix systems as well.) Unix has a
client/server protocol called "finger," by which you can contact
a server and ask for information about its users. I run "finger"
on my username at a local machine, and get:
% finger simon
Login name: simon (messages off) In real life: Simon Cozens
Office: Computing S
Directory: /v0/xzdg/simon Shell: /usr/local/bin/bash
On since Nov 6 10:03:46 5 minutes 38 seconds Idle Time
on pts/166 from riot-act.somewhere
On since Nov 6 12:28:08
on pts/197 from riot-act.somewhere
Project: Hacking Perl for Sugalski
Plan:
Insert amusing anecdote here.
What we're going to do is write our own finger client and a
server which dishes out information about the current system,
and we're going to do this using the object-oriented
IO::Socket module. Of course, we could do this
completely procedurally, using Socket.pm, but it's
actually comparatively much easier to do it this way.
First, the client. The finger protocol, as much as we need to care about it, is really simple. The client connects and sends a line of text -- generally, a username. The server sends back some text, and then closes the connection.
By using IO::Socket to manage the connection, we
can come up with something like this:
#!/usr/bin/perl
use IO::Socket::INET;
my ($host, $username) = @ARGV;
my $socket = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => "finger"
) or die $!;
$socket->print($username."\n");
while ($_ = $socket->getline) {
print;
}
This is pretty straightforward: the
IO::Socket::INET constructor new
gives us an object representing the connection to peer address
$host on port finger. We can then call
the print and getline methods to send
and receive data from the connection. Compare this with the
non-OO equivalent, and you may realize why people prefer dealing
with objects:
#!/usr/bin/perl -w
use strict;
use Socket;
my ($remote,$port, $iaddr, $paddr, $proto, $user);
($remote, $user) = @ARGV;
$port = getservbyname('finger', 'tcp') || die "no port";
$iaddr = inet_aton($remote) || die "no host: $remote";
$paddr = sockaddr_in($port, $iaddr);
$proto = getprotobyname('tcp');
socket(SOCK, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
connect(SOCK, $paddr) || die "connect: $!";
print SOCK "$user\n";
while (<SOCK>)) {
print;
}
close (SOCK) || die "close: $!";
Now, to turn to the server. We'll also use another OO module,
Net::hostent, which allows us to treat the result
of gethostbyaddr as an object, rather than as a
list of values. This means we don't have to worry about
remembering which element of the list means what we want.
#!/usr/bin/perl -w
use IO::Socket;
use Net::hostent;
my $server = IO::Socket::INET->new( Proto => 'tcp',
LocalPort => 'finger',
Listen => SOMAXCONN,
Reuse => 1);
die "can't setup server" unless $server;
while ($client = $server->accept()) {
$client->autoflush(1);
$hostinfo = gethostbyaddr($client->peeraddr);
printf "[Connect from %s]\n", $hostinfo->name || $client->peerhost;
my $command = client->getline();
if ($command =~ /^uptime/) { $client->print(`uptime`); }
elsif ($command =~ /^date/) { $client->print(scalar localtime, "\n"); }
else { $client->print("Unknown command\n");
$client->close;
}
This is chock-full of OO Perl goodness -- a method call on nearly
every line. We start in a very similar way to how we wrote the
client: using IO::Socket::INET->new as a constructor.
Did you notice anything strange about this?
IO::Socket::INET is a package name, which means it
must be a class, rather than an object. But we can still call
methods on classes (they're generally called "class methods," for
obvious reasons) and this is how most objects actually get
instantiated: the class provides a method called new
that produces an object for us to manipulate.
The big while loop calls the accept
method that waits until a client connects and, when one does,
returns another IO::Socket::INET object
representing the connection to the client. We can call the
client's autoflush method, which is the equivalent
to setting $| for its handle; the
peeraddr method returns the address of the client,
which we can feed to gethostbyaddr.
As we mentioned earlier, this isn't the usual Perl
gethostbyadd, but one provided by
Net::Hostent, and it returns yet another object!
We use the name method from this object, which
represents information about a given host, to find the host's name.
The rest isn't anything new. If you think back to our client, it sent a line and awaited a response -- so our server has to read a line, and send a response. You get bonus points for adding more possible responses to our server.
Conclusion
So there we are. We've seen a couple of examples of using object-oriented modules. It wasn't that bad, was it? Hopefully now you'll be well-enough equipped to be able to start using some of the many OO modules on CPAN for yourself.
If, on the other hand, you feel you need a little more in-depth coverage of OO Perl, you could take a look at the "perlboot," "perltoot," and "perltootc" pages in the Perl documentation. The Perl Cookbook, an invaluable book for any serious Perl programmer, has a very comprehensive and easy to follow treatment of OO techniques. Finally, the most in-depth treatment of all can be found in Damian Conway's "Object-Oriented Perl", which will see you through from a complete beginner way up to Perl 4 or 5 dan...

