ZetCode

Perl socket

last modified October 8, 2026

Perl socket tutorial shows how to work with sockets in Perl. Socket programming is low-level. The purpose of the tutorial is to introduce network programming including these low-level details. There are higher-level APIs that may be more practical in real-world scenarios.

Note: A network socket is a communication endpoint. For IP networking, its address includes an IP address and a transport-layer port.

Network protocols

TCP/IP is a suite of protocols used by devices to communicate over the Internet and most local networks. TCP provides a reliable, ordered byte stream; it does not preserve message boundaries. UDP sends independent datagrams without protocol-level guarantees of delivery or ordering. TCP is commonly used by HTTP, SMTP, and FTP, while UDP is often used for real-time traffic such as voice or video, where an application may prefer low latency over retransmission.

Perl socket modules

The Socket module provides networking constants and support functions.

IO::Socket::INET provides an object interface for IPv4 sockets. For new code that should support both IPv4 and IPv6, consider IO::Socket::IP, which is intended as its family-neutral replacement.

Perl UDP socket example

UDP sends independent datagrams. The protocol itself does not guarantee that they arrive or arrive in order. The Echo service has historically been defined for both TCP and UDP.

The Echo Protocol is a service in the Internet Protocol Suite defined in RFC 862. The Echo Protocol can use the TCP or the UDP on the port number 7. The server sends back an identical copy of the data it received.

We set up an echo service on a local Debian system.

$ cat /etc/services | grep echo | head -4
echo            7/tcp
echo            7/udp
echo            4/ddp                   # AppleTalk Echo Protocol

Port 7 is the well-known port assigned to the Echo service; the entry in /etc/services names the service but does not mean it is running.

Due to security concerns, the echo service is disabled in most cases on publicly available computers. Therefore, we create our own service in a local network.

For this example, start the echo service on the same isolated test machine.

# apt install xinetd

We install the xinetd package. The package provides the xinetd internet services daemon. The package and configuration shown here are distribution-dependent legacy setup; install and enable it only on an isolated test machine, never expose an Echo service to the public Internet.

...
# This is the udp version.
service echo
{
        disable         = no
        type            = INTERNAL
        id              = echo-dgram
        socket_type     = dgram
        protocol        = udp
        user            = nobody
        wait            = yes
}

In the /etc/xinetd.d/echo file, we set the disable option to no.

# systemctl start xinetd

We start the service.

echo_client.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $addr = 'localhost';
my $port = 7;

my $msg = shift || "hello";

my $sock = IO::Socket::INET->new(
    Domain => AF_INET,
    PeerAddr => $addr,
    PeerPort => $port,
    Proto => 'udp',
) or die "failed to create socket: $IO::Socket::errstr\n";

$sock->send($msg, 0) or die "failed to send datagram: $!\n";

my $data;
$sock->recv($data, 1024) or die "failed to receive datagram: $!\n";
print $data;

$sock->close();

The example sends a message to the echo service on a local network computer. The computer echoes back the message.

my $addr = 'localhost';
my $port = 7;

We define the network address and port.

my $sock = IO::Socket::INET->new(
    Domain => AF_INET,
    PeerAddr => $addr,
    PeerPort => $port,
    Proto => 'udp',
) or die "failed to create socket: $IO::Socket::errstr\n";

We create a new socket with IO::Socket::INET. We specify the domain, address, port, and protocol.

$sock->send($msg, 0);

We send a message on the socket.

my $data;
$sock->recv($data, 1024);
print $data;

We receive one UDP datagram with recv and print it. Unlike TCP, UDP preserves datagram boundaries; check both send and receive results in real code.

$sock->close();

We close the socket with close.

$ ./echo_client.pl cau
cau

Perl socket QOTD

The Quote of the Day (QOTD) service is a simple legacy diagnostic service that sends a short message. The service is commonly disabled; this example connects to a QOTD server listening on the local machine.

$ cat /etc/services | grep qotd
qotd            17/tcp          quote

Port 17 is the well-known port assigned to the Quote of the Day service.

qotd.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $host = shift || 'localhost';

my $sock = IO::Socket::INET->new(
    PeerHost => $host,
    PeerPort => 17,
    Proto => 'tcp',
) or die "failed to create socket: $IO::Socket::errstr\n";

while (<$sock>) {
    print $_;
}

print "\n";

$sock->close();

Set the optional host argument to a machine running QOTD. This legacy service is commonly disabled, so the example assumes a server has been configured.

my $host = shift || 'localhost';
my $sock = IO::Socket::INET->new(
    PeerHost => $host,
    PeerPort => 17,
    Proto => 'tcp',
) or die "failed to create socket: $IO::Socket::errstr\n";

A TCP connection is made to the host supplied on the command line (or localhost by default), on port 17. QOTD servers are uncommon and may be disabled.

$ ./qotd.pl qotd-server.local
A quote returned by the configured server

Perl socket WHOIS example

The WHOIS protocol can return domain-registration data, but the available fields vary by registry and may be redacted. For generic top-level domains, ICANN's Registration Data Access Protocol (RDAP) has replaced WHOIS as the required service since January 28, 2025. Use the registry's current lookup service rather than relying on a WHOIS server being available.

Traditional WHOIS servers use TCP on well-known port 43. For current registration-data lookups, prefer RDAP as described above.

This raw WHOIS example is retained to illustrate a line-oriented TCP protocol; its .me registry endpoint may change or stop accepting queries. For current generic-domain lookups, use an RDAP client or the registry's published RDAP endpoint. See ICANN's RDAP overview.

whs.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $domainName = shift || "example.me";
my $host = "whois.nic.me";
my $port = 43;

my $sock = IO::Socket::INET->new(
    PeerHost => $host,
    PeerPort => $port,
    Proto => 'tcp',
) or die "failed to create socket: $IO::Socket::errstr\n";

say 'socket created';

my $size = $sock->send("$domainName\n", 0);
say "Sent data of length: $size";

say '-------------------------';
 
while (<$sock>) {
    print $_;
}

close $sock;

In the example, we query a WHOIS service about the given domain.

$ ./whs.pl example.me
socket created
Sent data of length: ...
-------------------------
...registry-dependent response...

Perl socket HEAD request

A HEAD request asks for the same representation metadata as GET, but the server must not send response content. Headers can include metadata such as the content type and length.

head_req.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $addr = 'httpbin.org:80';
my $req = "HEAD /robots.txt HTTP/1.1\r\n" .
    "Host: httpbin.org\r\n" .
    "Connection: close\r\n\r\n";

my $sock = IO::Socket::INET->new(
    Domain => AF_INET,
    PeerAddr => $addr,
) or die "failed to create socket: $IO::Socket::errstr\n";

$sock->send($req, 0);

while (<$sock>) {
    print $_;
}

$sock->close();

In the example, we send a HEAD request for a text resource from httpbin.org.

$sock->send("HEAD /robots.txt HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n", 0);

This sends a minimal HTTP/1.1 request over unencrypted TCP. It supplies the required Host header and asks the server to close the connection so the client can read the response to EOF. HTTP uses CRLF to end each header line and a second CRLF to end the headers. Current HTTP semantics, including HEAD, are specified in RFC 9110. For HTTPS or production HTTP clients, use a higher-level module such as HTTP::Tiny instead of implementing the protocol manually.

$ ./head_req.pl 
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 30
Connection: close
...

Perl socket GET request

The HTTP GET method requests a representation of the specified resource. GET is defined as a safe method: a client requests retrieval of a representation, not a state change.

get_req.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $addr = 'httpbin.org:80';

my $req = "GET /robots.txt HTTP/1.1\r\n" .
    "Host: httpbin.org\r\n" .
    "User-Agent: Perl client\r\n" .
    "Connection: close\r\n\r\n";

my $sock = IO::Socket::INET->new(
    PeerAddr => $addr,
) or die "failed to create socket: $IO::Socket::errstr\n";

$sock->send($req, 0);

while (<$sock>) {
    print $_;
}

$sock->close();

This minimal raw-socket example uses unencrypted HTTP/1.1 on port 80 and reads a text response until the server closes the connection. Use HTTP::Tiny for HTTPS or general-purpose requests.

my $req = "GET /robots.txt HTTP/1.1\r\n" .
    "Host: httpbin.org\r\n" .
    "User-Agent: Perl client\r\n" .
    "Connection: close\r\n\r\n";

We write a simple GET request.

Perl socket send mail

This illustrates a minimal SMTP conversation with a local, unauthenticated server. It handles replies in sequence; real mail servers generally require TLS and authentication, and production code should use a maintained SMTP module instead. The example body is fixed and contains no lines beginning with a dot.

send_mail.pl
#!/usr/bin/perl

use 5.30.0;
use warnings;
use IO::Socket::INET;

my $from = 'john.doe@example.com';
my $to = 'root@localhost';
my $name = 'John Doe';
my $subject = 'Hello';
my $body = 'Hello there';
my $addr = 'localhost:25';

my $sock = IO::Socket::INET->new(
    PeerAddr => $addr,
    Proto => 'tcp',
) or die "failed to create socket: $IO::Socket::errstr\n";
$sock->autoflush(1);

sub read_response {
    my ($sock) = @_;
    my $line = <$sock>;
    defined $line or die "SMTP server closed the connection\n";
    my $code = substr($line, 0, 3);
    print $line;

    while ($line =~ /^\Q$code\E-/) {
        $line = <$sock>;
        defined $line or die "SMTP server closed the connection\n";
        print $line;
    }

    return ($code, $line);
}

sub send_command {
    my ($sock, $text, $expected) = @_;
    print {$sock} "$text\r\n" or die "Cannot send SMTP command: $!\n";
    my ($code, $line) = read_response($sock);
    $code eq $expected or die "Unexpected SMTP reply: $line";
}

my ($code) = read_response($sock);
$code eq '220' or die "Unexpected SMTP greeting: $code\n";
send_command($sock, 'HELO localhost', '250');
send_command($sock, "MAIL FROM:<$from>", '250');
send_command($sock, "RCPT TO:<$to>", '250');
send_command($sock, 'DATA', '354');

print {$sock} "From: $name <$from>\r\n" .
    "To: $to\r\n" .
    "Subject: $subject\r\n\r\n" .
    "$body\r\n.\r\n" or die "Cannot send message: $!\n";
my ($data_code, $data_line) = read_response($sock);
$data_code eq '250' or die "Message was not accepted: $data_line";
send_command($sock, 'QUIT', '221');

$sock->close();

Run this only when a local SMTP server is configured to accept unauthenticated mail. Replies and message-delivery details vary by server.

$ ./send_mail.pl
220 ...
250 ...
250 ...
250 ...
354 ...
250 ...
221 ...

The submitted message has the following headers and body:

From: John Doe <john.doe@example.com>
To: root@localhost
Subject: Hello

Hello there

In this article we have worked with sockets in Perl.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.