Sockets

shutdown#

Shut down one direction of a socket connection, or both.

shutdown tells the kernel to stop a socket doing further reads, further writes, or both — without closing the file descriptor. Unlike close, the effect is immediate and propagates through every forked copy of the descriptor, not just this process’s. HOW has the same meaning as the argument to the shutdown(2) syscall.

Synopsis#

shutdown SOCKET, HOW

What you get back#

1 on success. On failure, undef if SOCKET is not a valid filehandle, or 0 with $! set for any other error (the socket is not connected, already shut down in that direction, etc.).

Two distinct failure returns, so a three-way test is sometimes warranted:

my $r = shutdown($sock, 1);
if    (!defined $r) { die "bad filehandle" }
elsif (!$r)         { die "shutdown failed: $!" }

The HOW argument#

HOW is a small integer matching the POSIX SHUT_* constants:

  • 0 — stop reading. The peer can still send, but data will be discarded by the kernel and read / sysread on this end will return end-of-file.

  • 1 — stop writing. Queued output is flushed, then the peer sees end-of-file on its read side. print / syswrite on this end will fail and raise SIGPIPE.

  • 2 — stop both directions. Equivalent to calling shutdown with 0 and then 1.

The symbolic names SHUT_RD, SHUT_WR, SHUT_RDWR live in Socket if you prefer readable code:

use Socket qw(SHUT_WR);
shutdown $sock, SHUT_WR;

shutdown vs close#

close drops the file descriptor in the current process only. Other processes that inherited the descriptor through fork keep their copies open, and the peer does not see end-of-file until the last copy is closed.

shutdown acts on the underlying socket, not on the descriptor. After shutdown($sock, 1) every forked copy sees the write half closed and the peer reads end-of-file right away — even though the descriptor itself is still open and can still be read from.

That is the usual reason to reach for shutdown: a parent and child share a socket, the child has finished sending, and the parent must keep reading replies. close in the child would be invisible to the peer; shutdown($sock, 1) is not.

Half-close for request/response protocols#

A client that sends a complete request and then needs the server to know there is no more input uses shutdown with HOW = 1:

use IO::Socket::INET;
my $sock = IO::Socket::INET->new(PeerAddr => "example.com:80")
    or die "connect: $!";

print $sock "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
shutdown $sock, 1;          # signal end-of-request to the server

while (my $line = <$sock>) {
    print $line;
}
close $sock;

Without the shutdown, servers that read until end-of-input would block waiting for more request data.

Examples#

Stop reading but keep writing — the peer may continue to send, we just will not look at it:

shutdown $sock, 0;

Stop writing, keep reading — typical half-close after finishing a request:

shutdown $sock, 1;

Full shutdown, then close the descriptor:

shutdown $sock, 2;
close $sock;

Using Socket constants for readability:

use Socket qw(SHUT_RD SHUT_WR SHUT_RDWR);
shutdown $sock, SHUT_WR;

Check the specific error when a shutdown fails on an already-closed direction:

unless (shutdown $sock, 1) {
    warn "shutdown write failed: $!";
}

Edge cases#

  • Writing after shutdown($sock, 1) fails and raises SIGPIPE. The default action terminates the process; install $SIG{PIPE} = 'IGNORE' (or a handler) to convert it into a normal write failure you can check.

  • Reading after shutdown($sock, 0) returns end-of-file immediately, not an error. Any data already queued in the kernel’s receive buffer is discarded.

  • Not a socket: calling shutdown on a regular filehandle sets $! to ENOTSOCK and returns 0.

  • Not connected: shutdown on a socket that was never connected (a fresh socket result, or a listening socket) sets $! to ENOTCONN and returns 0.

  • Double shutdown: shutting down the same half twice typically returns 0 with ENOTCONN. Portable code ignores the second failure rather than treating it as fatal.

  • Closed filehandle: returns undef; under use warnings a shutdown() on closed socket warning is emitted.

  • Descriptor is not closed: shutdown disables the directions you asked for but leaves the file descriptor open. Call close (or let the handle go out of scope) when you are actually done with it.

  • Shared descriptors across fork: this is the case shutdown is designed for. All processes holding the descriptor see the shutdown take effect immediately; they do not get separate views.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • close — release the descriptor in this process; does not affect forked copies and does not signal end-of-file to the peer until the last copy closes

  • socket — create the socket that shutdown operates on

  • connect — establish the connection; shutdown on an unconnected socket fails with ENOTCONN

  • accept — produces the connected socket on the server side that typically gets half-closed with shutdown

  • Socket — exports SHUT_RD, SHUT_WR, SHUT_RDWR and the rest of the socket-level constants

  • $! — system error string to check after a 0 return