TCP sockets#
A TCP socket carries a reliable, ordered byte stream between two processes that may be on different machines. This page builds the two halves of a TCP conversation from the raw built-ins: a client that connects and exchanges a line, and a forking server that accepts connections and handles each in its own child.
The high-level convenience module IO::Socket::INET is not available in PetaPerl, so every recipe here uses the built-in socket layer directly with address helpers from the Socket module. That is no loss: the built-in form is what IO::Socket::INET wraps, and seeing it in the open makes the handshake legible.
The address helpers#
A socket address is a packed C struct sockaddr_in. The Socket module builds and takes apart that structure for you:
use Socket;
my $packed = sockaddr_in($port, $ip); # build
my ($port, $ip) = sockaddr_in($packed); # take apart
$ip is itself a packed four-byte address. inet_aton turns a dotted-quad string into one; inet_ntoa turns it back. The constant INADDR_LOOPBACK is the packed form of 127.0.0.1, and INADDR_ANY means “every local interface”.
use Socket;
my $ip = inet_aton("127.0.0.1");
print inet_ntoa($ip), "\n"; # 127.0.0.1
A TCP client#
The client creates a socket, connects it to a server address, and then reads and writes the handle like any other stream.
use Socket;
my $host = "127.0.0.1";
my $port = 8080;
socket(my $sock, PF_INET, SOCK_STREAM, getprotobyname("tcp"))
or die "socket: $!";
my $addr = sockaddr_in($port, inet_aton($host));
connect($sock, $addr) or die "connect: $!";
$sock->autoflush(1); # send each line as it is printed
print $sock "ping\n";
my $reply = <$sock>;
print "server said: $reply"; # server said: pong: ping
close $sock;
The one line that catches everyone is $sock->autoflush(1). Without it, print $sock "ping\n" sits in Perl’s output buffer, the server never sees it, and both sides block forever waiting on each other. On any socket where you write a request and then read a reply, set autoflush before the first write.
A forking server#
A server binds to a port, listens, and then loops accepting connections. The classic structure handles each client in a forked child so one slow client cannot stall the next:
use Socket;
use POSIX qw(WNOHANG); # for the non-blocking reaper
my $port = 8080;
my $backlog = 128; # pending-connection queue depth
socket(my $server, PF_INET, SOCK_STREAM, getprotobyname("tcp"))
or die "socket: $!";
# Let the address be reused immediately after a restart.
setsockopt($server, SOL_SOCKET, SO_REUSEADDR, 1)
or die "setsockopt: $!";
bind($server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";
listen($server, $backlog) or die "listen: $!";
# Reap finished children so they do not become zombies.
$SIG{CHLD} = sub { 1 while waitpid(-1, WNOHANG) > 0 };
print "listening on port $port\n";
while (accept(my $client, $server)) {
my $pid = fork();
die "fork: $!" unless defined $pid;
if ($pid == 0) {
# Child: serve this one connection, then exit.
close $server; # child does not need the listener
$client->autoflush(1);
while (my $line = <$client>) {
print $client "pong: $line";
}
close $client;
exit 0;
}
# Parent: hand the connection to the child and move on.
close $client; # parent does not need this client
}
Three details make this server correct rather than merely working:
SO_REUSEADDRbeforebind. After a server exits, the kernel keeps the port inTIME_WAITfor a while. Without this option a restart fails with “Address already in use”. Set it and the restart binds immediately.The
$SIG{CHLD}reaper. Every forked child becomes a zombie when it exits until the parent callswaitpid. The handler reaps all finished children non-blockingly withWNOHANG, so the accept loop never pauses to wait.Both sides close the descriptor they do not use. The child closes the listening socket; the parent closes the accepted client socket. If either skips its
close, the underlying file descriptor stays open in the wrong process and the connection never sees EOF.
Running both halves together#
For a self-contained demonstration, fork the server into the background and run the client in the parent. The server binds to an ephemeral port (port 0) and reports the port the kernel assigned:
use Socket;
# --- server child on an ephemeral port ---
socket(my $server, PF_INET, SOCK_STREAM, getprotobyname("tcp"))
or die "socket: $!";
setsockopt($server, SOL_SOCKET, SO_REUSEADDR, 1) or die "setsockopt: $!";
bind($server, sockaddr_in(0, INADDR_LOOPBACK)) or die "bind: $!";
listen($server, 128) or die "listen: $!";
my ($port) = sockaddr_in(getsockname($server));
my $kid = fork() // die "fork: $!";
if ($kid == 0) {
accept(my $client, $server) or die "accept: $!";
$client->autoflush(1);
my $line = <$client>;
print $client "pong: $line";
close $client;
exit 0;
}
# --- client in the parent ---
socket(my $sock, PF_INET, SOCK_STREAM, getprotobyname("tcp")) or die;
connect($sock, sockaddr_in($port, INADDR_LOOPBACK)) or die "connect: $!";
$sock->autoflush(1);
print $sock "ping\n";
print "client got: ", scalar <$sock>; # client got: pong: ping
close $sock;
waitpid($kid, 0);
getsockname($server) reports the local address the socket is bound to. Unpacking it with sockaddr_in recovers the port the kernel chose, which the client then connects to - the standard way to write a test that does not hard-code a port.
Reference cross-links#
Each call has a per-built-in reference page with the full argument shape and return value:
socket- create the endpointbind- attach it to a local addresslisten- mark it as accepting connectionsaccept- take the next pending connectionconnect- reach out to a serversetsockopt- setSO_REUSEADDRand friendsgetsockname- discover the bound port%SIG- theCHLDreaper handler
Next#
For local-only stream sockets without the network stack, and for connectionless datagrams, see UNIX-domain and UDP sockets.