UNIX-domain and UDP sockets#
Not every socket reaches across a network. This page covers two lighter-weight relatives of the TCP socket from the previous page: a UNIX-domain socket pair for two related processes on one machine, and a UDP socket for connectionless datagrams.
Both use the same socket family of built-ins and the same Socket address helpers. The high-level IO::Socket::* wrappers are not available in PetaPerl, so the recipes stay at the built-in layer.
A socket pair for parent and child#
socketpair creates two connected sockets in one call, with no addresses, no bind, and no connect. It is the ideal channel between a parent and a child it is about to fork: full-duplex (both ends can read and write), local, and already connected the moment it returns.
use Socket;
socketpair(my $child_end, my $parent_end, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
or die "socketpair: $!";
my $kid = fork() // die "fork: $!";
if ($kid == 0) {
# Child keeps its end, closes the parent's.
close $parent_end;
$child_end->autoflush(1);
print $child_end "child-says-hi\n";
my $back = <$child_end>;
print "child got: $back"; # child got: ack
exit 0;
}
# Parent keeps its end, closes the child's.
close $child_end;
$parent_end->autoflush(1);
my $msg = <$parent_end>;
print "parent got: $msg"; # parent got: child-says-hi
print $parent_end "ack\n";
waitpid($kid, 0);
Why reach for socketpair over a plain pipe? A pipe is one-directional - the reader reads, the writer writes, and that is fixed at creation. A socket pair is bidirectional: each end both reads and writes, so a request-and-reply exchange needs one socketpair call instead of two pipes wired in opposite directions.
The autoflush rule from TCP applies unchanged: set it on each end before the first write, or a buffered request never reaches the other side and both processes block.
A UDP datagram exchange#
UDP is connectionless. There is no connect, no accept, no stream - just discrete messages, each addressed to a destination and each preserving its own boundaries. A receiver binds to a port; a sender fires a datagram at it with send; the receiver collects it with recv, which also reports who sent it.
use Socket;
# --- receiver on an ephemeral port ---
socket(my $server, PF_INET, SOCK_DGRAM, getprotobyname("udp"))
or die "socket: $!";
bind($server, sockaddr_in(0, INADDR_LOOPBACK)) or die "bind: $!";
my ($port) = sockaddr_in(getsockname($server));
my $kid = fork() // die "fork: $!";
if ($kid == 0) {
# Sender fires one datagram at the receiver's port.
socket(my $client, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die;
my $to = sockaddr_in($port, INADDR_LOOPBACK);
send($client, "hello-udp", 0, $to) or die "send: $!";
exit 0;
}
# recv fills $buf and returns the sender's packed address.
my $from = recv($server, my $buf, 65535, 0);
my ($sender_port, $sender_ip) = sockaddr_in($from);
print "got '$buf' from ", inet_ntoa($sender_ip), "\n";
# got 'hello-udp' from 127.0.0.1
waitpid($kid, 0);
What UDP gives you and what it does not:
Message boundaries are preserved. One
sendis onerecv. Arecvreturns exactly one datagram, never two concatenated and never half of one. The65535is the maximum length you are willing to accept; a shorter datagram returns its real length.There is no
connectand no handshake. The sender does not open a connection; it addresses each datagram withsend’s fourth argument.recvreports the sender’s address so a reply can go back the same way.Delivery is not guaranteed. UDP datagrams can be dropped, duplicated, or reordered by the network. Over the loopback interface, as in this recipe, that effectively never happens; over a real network you must handle loss yourself if it matters.
Reference cross-links#
socketpair- two connected local socketssocket- create a datagram or stream endpointbind- attach the receiver to a local portsend- fire a datagram at an addressrecv- collect a datagram and its sendergetsockname- discover the bound port
Next#
For shared memory and semaphores - channels the kernel owns rather than streams between processes - see System V IPC.