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.