Sockets and IO::Socket
| A TCP client | |
| A TCP server | |
| Timeouts with IO::Select | |
| Framing and safety |
A TCP client
A socket is a file handle connected to another program. IO::Socket::INET ships with Perl and turns the connection details into one constructor call.
use strict;
use warnings;
use IO::Socket::INET;
my $sock = IO::Socket::INET->new(
PeerHost => "example.com",
PeerPort => 80,
Proto => "tcp",
Timeout => 5,
) or die "connect failed: $!\n";
print $sock "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
while (my $line = <$sock>) {
print $line;
}
close $sock;
This is HTTP by hand: the request line, one header, a blank line, then the response. Reading until end of file works here because the server closes the connection after one response — with HTTP/1.1 you would have to parse the length header instead.
A TCP server
A server binds a local port and then calls accept in a loop. Each accepted socket is a separate conversation, so the server can keep listening while it talks to a client.
my $server = IO::Socket::INET->new(
LocalAddr => "127.0.0.1",
LocalPort => 9000,
Proto => "tcp",
Listen => 5,
Reuse => 1,
) or die "cannot listen on 9000: $!\n";
print "listening on 127.0.0.1:9000\n";
while (my $client = $server->accept) {
$client->autoflush(1);
print "client connected from ", $client->peerhost, "\n";
while (my $line = <$client>) {
chomp $line;
print $client "echo: $line\n";
last if $line eq "quit";
}
close $client;
}
Reuse => 1 lets you restart the server without waiting for the old port to leave the TIME_WAIT state. autoflush matters: without it the reply may sit in a buffer while the client waits forever.
This server handles one client at a time. For several at once, accept and then fork, or use a module that does the event loop for you, such as IO::Socket::INET plus IO::Select, or a framework like Mojo.
Timeouts with IO::Select
Reading from a socket blocks until data arrives. If the other side goes silent, your program waits. IO::Select asks whether a handle is readable within a time limit:
use IO::Select;
my $sel = IO::Select->new($sock);
if ($sel->can_read(5)) {
my $line = <$sock>;
print "got: $line";
} else {
warn "no answer in 5 seconds\n";
}
The same object can watch many sockets at once and return the readable ones, which is a compact way to write a server for a handful of clients. can_write works the same way for sending.
Framing and safety
TCP is a stream of bytes, not a sequence of messages. One write can arrive as two reads, and two writes can arrive as one. A protocol has to mark where a message ends.
The simplest framing is a newline after every message, which is what many line-based protocols do. For arbitrary binary data, prefix the payload with its length and read exactly that many bytes:
my $len = pack("N", length $payload);
print $sock $len, $payload;
Switch such a handle to binary mode with binmode $sock, otherwise Perl may translate line endings on some systems and corrupt the bytes. On the reading side, use read in a loop until you have the announced count.
Bind to 127.0.0.1 while learning. Binding to 0.0.0.0 exposes the service to the network, and a small echo server without authentication is an open relay for anything that finds the port. Check peerhost only if you control the network: addresses are easy to spoof.
Next: Databases with DBI
Article author: Arthur Isaev