Perl 套接字
最后修改于 2023 年 8 月 24 日
Perl 套接字教程展示了如何在 Perl 中使用套接字。套接字编程是低级别的。本教程的目的是介绍网络编程,包括这些低级细节。还有一些更高级的 API,在实际场景中可能更实用。
网络协议
TCP/IP 是一套协议,用于设备在 Internet 和大多数本地网络上进行通信。TCP 更可靠,具有广泛的错误检查,并且需要更多资源。HTTP、SMTP 或 FTP 等服务使用 TCP。UDP 可靠性差得多,错误检查有限,并且需要更少的资源。VoIP 等服务使用 UDP。
Perl 套接字模块
Socket 模块提供了网络常量和支持函数。
IO::Socket::INET 提供了创建和使用 AF_INET 域中套接字的面向对象接口;它与 IPv4 地址一起使用。IO::Socket::IP 是一个用于处理 IPv4 和 IPv6 地址的模块。
Perl UDP 套接字示例
UDP 是一种通信协议,通过网络传输独立的数据包,不保证到达,也不保证传递顺序。回显是一项使用 UDP 的服务。
回显协议是 RFC 862 中定义的 Internet 协议套件中的一项服务。回显协议可以使用 TCP 或 UDP,端口号为 7。服务器会发送回它接收到的数据的相同副本。
我们在本地 Debian 系统上设置了一个回显服务。
$ cat /etc/services | grep echo | head -4 echo 7/tcp echo 7/udp echo 4/ddp # AppleTalk Echo Protocol
端口 7 保留给回显服务。
由于安全原因,在公开可用的计算机上,回显服务在大多数情况下是禁用的。因此,我们在本地网络中创建自己的服务。
我们在本地网络中的另一台计算机上启动回显服务。
# apt install xinetd
我们安装了 xinetd 包。该包包含 xinetd 守护进程,它是一个 TCP 包装的超级服务,用于访问回显、FTP、IMAP 和 telnet 等部分常用网络服务。
...
# This is the udp version.
service echo
{
disable = no
type = INTERNAL
id = echo-dgram
socket_type = dgram
protocol = udp
user = root
wait = yes
}
在 /etc/xinetd.d/echo 文件中,我们将 disable 选项设置为 no。
# systemctl start xinetd
我们启动了该服务。
#!/usr/bin/perl
use 5.30.0;
use warnings;
use IO::Socket::INET;
my $addr = 'core9';
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: $!\n";
$sock->send("$msg\n", 0);
my $data = <$sock>;
say "$data";
$sock->close();
该示例将消息发送到本地网络计算机上的回显服务。该计算机将消息回显回来。
my $addr = 'core9'; my $port = 7;
我们定义了网络地址和端口。
my $sock = IO::Socket::INET->new(
Domain => AF_INET,
PeerAddr => $addr,
PeerPort => $port,
Proto => 'udp',
) or die "failed to create socket: $!\n";
我们使用 IO::Socket::INET 创建了一个新的套接字。我们指定了域、地址、端口和协议。
$sock->send("$msg\n", 0);
我们在套接字上发送了一条消息。
my $data = <$sock>; say "$data";
我们读取并打印了响应。
$sock->close();
我们使用 close 关闭了套接字。
$ ./echo_client.pl cau cau
Perl 套接字 QOTD
每日名言服务是一个调试和测量工具。每日名言服务只是发送一条简短的消息,而不考虑输入。
$ cat /etc/services | grep qotd qotd 17/tcp quote
端口 17 保留给每日名言服务。
#!/usr/bin/perl
use 5.30.0;
use warnings;
use IO::Socket::INET;
my $sock = IO::Socket::INET->new("djxmmx.net:17")
or die "failed to create socket: $!\n";
$sock->send('', 0);
while (<$sock>) {
print $_;
}
print "\n";
$sock->close();
该示例创建了一个连接到 QOTD 服务的客户端程序。
my $sock = IO::Socket::INET->new("djxmmx.net:17")
or die "failed to create socket: $!\n";
会创建一个 TCP 套接字连接到指定的地址和端口。如果未指定协议,则假定为 TCP。请注意,此类服务是短暂的;它们可能随时被移除。
$sock->send('', 0);
我们向套接字发送了一条空消息。
$ ./qotd.pl
"Here's the rule for bargains: "Do other men, for they would do you."
That's the true business precept." Charles Dickens (1812-70)
Perl WHOIS 套接字示例
WHOIS 服务允许我们查找有关域名注册的信息,例如注册日期和域名年龄,或域名所有者联系方式。
在现代 Internet 中,WHOIS 服务通常使用传输控制协议 (TCP) 进行通信。服务器在知名端口号 43 上监听请求。
请注意,whoise 服务通常仅限于一小组域名。
#!/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: $!\n";
say 'socket created';
my $size = $sock->send("$domainName\n", 0);
say "Sent data of length: $size";
say '-------------------------';
while (<$sock>) {
print $_;
}
close $sock;
在示例中,我们查询了给定域名的 WHOIS 服务。
$ ./whs.pl tada.me socket created Sent data of length: 8 ------------------------- Domain Name: TADA.ME Registry Domain ID: D425500000006230617-AGRS Registrar WHOIS Server: whois.godaddy.com Registrar URL: http://www.godaddy.com Updated Date: 2021-01-11T19:57:02Z Creation Date: 2017-09-09T18:21:42Z Registry Expiry Date: 2021-09-09T18:21:42Z ...
Perl 套接字 HEAD 请求
HEAD 请求是一种 HTTP GET 请求,但不包含消息正文。请求/响应的头部包含元数据,例如 HTTP 协议版本或内容类型。
#!/usr/bin/perl
use 5.30.0;
use warnings;
use IO::Socket::INET;
my $addr = 'webcode.me:80';
my $req = "HEAD / HTTP/1.0\r\n\r\n";
my $sock = IO::Socket::INET->new(
Domain => AF_INET,
PeerAddr => $addr,
) or die "failed to create socket: $!\n";
$sock->send($req, 0);
while (<$sock>) {
print $_;
}
$sock->close();
在示例中,我们向 webcode.me 发送了一个 HEAD 请求。
$sock->send("HEAD / HTTP/1.0\r\n\r\n", 0);
HEAD 请求使用 HEAD 命令后跟资源 URL 和 HTTP 协议版本发出。请注意,\r\n 字符是通信过程中强制性的部分。详细信息在 RFC 7231 文档中进行了描述。
$ ./head_req.pl HTTP/1.1 200 OK Server: nginx/1.6.2 Date: Wed, 30 Jun 2021 12:57:05 GMT Content-Type: text/html Content-Length: 348 Last-Modified: Sat, 20 Jul 2019 11:49:25 GMT Connection: close ETag: "5d32ffc5-15c" Accept-Ranges: bytes
Perl 套接字 GET 请求
HTTP GET 方法请求指定资源的表示。使用 GET 的请求应该只检索数据。
#!/usr/bin/perl
use 5.30.0;
use warnings;
use IO::Socket::INET;
my $addr = 'webcode.me:80';
my $req = "GET / HTTP/1.0\r\n" .
"Host: webcode.me\r\n" .
"User-Agent: Perl client\r\n\r\n";
my $sock = IO::Socket::INET->new(
PeerAddr => $addr,
) or die "failed to create socket: $!\n";
$sock->send($req, 0);
while (<$sock>) {
print $_;
}
$sock->close();
该示例使用 GET 请求读取 webcode.me 的主页。
my $req = "GET / HTTP/1.0\r\n" .
"Host: webcode.me\r\n" .
"User-Agent: Perl client\r\n\r\n";
我们编写了一个简单的 GET 请求。
Perl 套接字发送邮件
要通过套接字发送电子邮件,我们使用 SMTP 命令,例如 HELO、MAIL FROM 和 DATA。
#!/usr/bin/perl
use 5.30.0;
use warnings;
use IO::Socket::INET;
my $from = 'john.doe@example.com';
my $to = 'root@core9';
my $name = 'John Doe';
my $subject = 'Hello';
my $body = 'Hello there';
my $addr = 'core9:25';
my $req = "HELO core9\r\n" .
"MAIL FROM: $from\r\n" .
"RCPT TO: $to\r\n" .
"DATA\r\n" .
"From: $name\r\n" .
"Subject: $subject \r\n" .
"$body\r\n.\r\n" . "QUIT\r\n";
my $sock = IO::Socket::INET->new(
PeerAddr => $addr,
) or die "failed to create socket: $!\n";
$sock->send($req, 0);
while (<$sock>) {
print $_;
}
$sock->close();
该示例将电子邮件发送到本地网络上托管邮件服务器的计算机。
$ ./send_mail.pl 220 core9 ESMTP Sendmail 8.15.2/8.15.2; Thu, 1 Jul 2021 14:27:19 +0200 (CEST) 250 core9 Hello spartan.local [192.168.0.20], pleased to meet you 250 2.1.0 john.doe@example.com... Sender ok 250 2.1.5 root@core9... Recipient ok 354 Enter mail, end with "." on a line by itself 250 2.0.0 161CRJJv001451 Message accepted for delivery 221 2.0.0 core9 closing connection
我们发送了电子邮件。
From john.doe@example.com Thu Jul 1 14:27:19 2021 Return-Path: <john.doe@example.com> Received: from core9 (spartan.local [192.168.0.20]) by core9 (8.15.2/8.15.2) with SMTP id 161CRJJv001451 for root@core9; Thu, 1 Jul 2021 14:27:19 +0200 (CEST) (envelope-from john.doe@example.com) Date: Thu, 1 Jul 2021 14:27:19 +0200 (CEST) Message-Id: <202107011227.161CRJJv001451@core9> From: John.Doe Subject: Hello To: undisclosed-recipients:; Status: RO Hello there
我们在接收端检查了电子邮件。
在本文中,我们学习了如何在 Perl 中使用套接字。
作者
列出 所有 Perl 教程。