Socket programming in Perl: Part II

Building upon our earlier review of the socket.pm module, we will now analyze the points of IO::Socket. These are subclasses of IO::Socket which are specific to the domain, but they all inherit the methods defined within IO::Socket. I should also mention that the IO::Socket subclass is one born of the IO::Handle interface, and consequently, it inherits all of its methods methods. The real meat-and-potatoes are found within the IO::Socket::INET and IO::Socket::UNIX subclasses; however, there are a few methods within IO::Socket that provide quick front end functionality for any socket. The notable ones are listed below, but one may refer to CPAN for the exhaustive list:

protocol     //returns numerical socket protocol
accept()     //returns an object that one may use to communicate with a client socket
socktype     //returns numerical socket type
connected     //returns peer address if connected or undef if not connected
sockdomain     //returns numerical domain type
timeout()

Let's quickly move on to the IO::Socket::INET subclass so that we may see a socket in action. If this pace seems a little quick for you, then I would recommend revisiting Part 1 of this article. That being said, let's move on!

use IO::Socket;

$socket = IO::Socket::INET->new(PeerAddr => $remote\_host,
                                PeerPort => $remote\_port,
                                Proto    => "tcp",
                                Type     => SOCK\_STREAM)
                or die "$@\\n";

As you can see there isn't a lot that we're doing differently here than what we've already done using Socket. The constructor will return undef if anything untoward occurs, and the variable !@ will be initialized with the error string. When using the tcp protocol, connect() will be called unless Listen() is evoked within the arguments of the constructor. The $remote_host variable may be either a hostname or IP that ARP may resolve (or not, and $socket will be undef :) The $remote_port may be a port number, but one can imbed the port number in the $remote_host address when appended using a colon ($remote_host = "cyberarmy.net:80"). The additional key-value pairs provided by the IO::Socket::INET constructor are as follows

PeerAddr    Remote host address          <hostname>\[:<port>\]
      PeerHost    Synonym for PeerAddr
      PeerPort    Remote port or service       <service>\[(<no>)\] | <no>
      LocalAddr   Local host bind address      hostname\[:port\]
      LocalHost   Synonym for LocalAddr
      LocalPort   Local host bind port         <service>\[(<no>)\] | <no>
      Proto       Protocol name (or number)    "tcp" | "udp" | ...
      Type        Socket type                  SOCK\_STREAM | SOCK\_DGRAM | ...
      Listen      Queue size for listen
      ReuseAddr   Set SO\_REUSEADDR before binding
      Reuse       Set SO\_REUSEADDR before binding (deprecated, prefer ReuseAddr)
      ReusePort   Set SO\_REUSEPORT before binding
      Broadcast   Set SO\_BROADCAST before binding
      Timeout     Timeout value for various operations
      MultiHomed  Try all addresses for multi-homed hosts
      Blocking    Determine if connection will be blocking mode

The variable Proto shares a couple weird relationships that some might find really convenient (or totally frustrating if unaware of the relationship). Firstly, Type may be initalized by the Proto defined. That is, TCP would obviously initialize Type as SOCK_STREAM if Type was not initialized explicitly. Also, one may pass a symbolic service name to the constructor in a way to initialize the port number. If done in this way, IO::Socket::INET will initialize Proto to accomodate the protocol upon which the service communicates. For those of you who are familiar with the protocols associated with more common services, this will make sense to you. For those of you who aren't, check out http://www.iana.org/assignments/port-numbers. The methods included in the subclass are listed below.

sockaddr()    returns the address of the socket
peeraddr ()     returns the address of the peer sockaddrReturn the address part of the sockaddr structure for the socket on the peer host
sockport ()     returns the port number upon which the socket is listening or is connected
peerport ()     returns the peer port numberReturn the port number for the socket on the peer host.
sockhost ()     returns the address of sockaddr in hex
peerhost ()     returns the address of the peer sockaddr in hex

We'll now quickly analyze the IO::Socket::UNIX subclass, it's key-value pair arguments to the constructor, and the methods of the subclass. The constructor key-value pairs are listed below.

Type        Type of socket
Local       Path to local first in/first out
Peer        Path to peer first in/first out
Listen      Create a listen socket

The two methods used with the IO::Socket::UNIX subclass are hostpath() and peerpath(). hostpath() returns the filepath to the local fifo, while peerpath() returns the filepath to the peer fifo.

Let's go on and build some sockets, shall we? We already created a TCP client socket object that handily doubles as a filehandle. Therefore, once we've created the $socket object, we can communicate as easily as this...

print $socket "Someone set us up the bomb\\n";
$make\_your\_time = <$socket>;

This won't work, obviously, with a UDP connection. Instead one would still have to use the send() and recv() methods. Rather than go through all of the steps creating a Socket.pm socket, everything is conveniently done using the IO::Socket::INET constructor. Now, let's look at a TCP server.

use IO::Socket;
$server\_sock = IO::Socket::INET->new(LocalPort => $server\_sock\_port,
                                Type      => SOCK\_STREAM,
                                Listen    => SOMAXCONN,
                                Reuse     => 1)
    or die "$@\\n";
STDOUT->autoflush(1);

while ($client = $server\_sock->accept()) {
        while(<$server\_sock>){
                print "$\_\\n";}
}
close($server);

The only new element here is the expression, "STDOUT->autoflush(1);", which makes socket IO hot rather than buffering input before handling it. This provides for a more interactive IO communication between sockets. This expression will produce the aforementioned environment if you're using Perl5, but if you're using Perl4 it would be expressed as

select(( select(STDOUT), $| = 1 )\[0\]);

or thereabouts. Lol, it's been a while. One can use the IO::Socket subclass constructor to create a UDP object quite easily, but one is reduced to communicating on a UDP socket using the built-in methods that we discussed in Part 1. Now let's quickly construct a UDP client and server.

UDP client

use IO::Socket;
$client = IO::Socket::INET->new(Proto => 'udp') 
    or die "socket: $@";

UDP Server

use IO::Socket;
$server = IO::Socket::INET->new(LocalPort => $server\_port,
                                Proto     => "udp")
    or die "$@\\n";
while ($peer = $server->recv($datagram, $MAX\_TO\_READ, $flags)) {
        my($port, $ipaddr) = sockaddr\_in($sock->peername);
        $peer\_host = gethostbyaddr($ipaddr, AF\_INET);
        print "$peer\_host: $datagram\\n";
}

Well that about wraps it up. I'll eventually get around to reviewing a couple of the other subclasses including SSL and Multicast. In the meantime remember, there's more than one way to do it, damnit.