IO::Socket - 套接字通信的对象接口
use strict;
use warnings;
use IO::Socket qw(AF_INET AF_UNIX);
# create a new AF_INET socket
my $sock = IO::Socket->new(Domain => AF_INET);
# which is the same as
$sock = IO::Socket::INET->new();
# create a new AF_UNIX socket
$sock = IO::Socket->new(Domain => AF_UNIX);
# which is the same as
$sock = IO::Socket::UNIX->new();
IO::Socket
提供了一个面向对象的、基于 IO::Handle 的接口,用于通过 Socket 创建和使用套接字,后者提供了一个与 C 套接字库几乎一一对应的接口。
IO::Socket
是一个基类,实际上只定义了那些对所有类型的套接字都通用的操作的方法。特定于特定套接字域的操作在 IO::Socket
的子类中定义了方法。有关此类子类的示例,请参见 IO::Socket::INET、IO::Socket::UNIX 和 IO::Socket::IP。
IO::Socket
将导出 Socket 定义的所有函数(和常量)。
鉴于 IO::Socket
在传统意义上没有属性,因此可以将以下参数(而不是属性)传递给构造函数。
构造函数参数应以 Key => 'Value'
对的形式传递。
唯一必需的参数是 "Domain" in IO::Socket。
my $sock = IO::Socket->new(..., Blocking => 1);
$sock = IO::Socket->new(..., Blocking => 0);
如果定义但为 false,则套接字将设置为非阻塞模式。如果未指定,则默认为 1
(阻塞模式)。
my $sock = IO::Socket->new(Domain => IO::Socket::AF_INET);
$sock = IO::Socket->new(Domain => IO::Socket::AF_UNIX);
套接字域将定义使用 IO::Socket
的哪个子类。此发行版中提供的两个选项是 AF_INET
和 AF_UNIX
。
AF_INET
用于 Internet 地址族套接字,并通过 IO::Socket::INET 处理。AF_INET
套接字绑定到 Internet 地址和端口。
AF_UNIX
用于 Unix 域套接字,并通过 IO::Socket::UNIX 处理。AF_UNIX
套接字绑定到文件系统作为其地址名称空间。
此参数是必需的。所有其他参数都是可选的。
my $sock = IO::Socket->new(..., Listen => 5);
侦听应为整数或留空。
如果提供,此参数将使套接字进入侦听模式。然后可以使用 "accept" in IO::Socket 方法接受新连接。给定的值用作 listen(2)
队列大小。
如果给出了 Listen
参数,但为 false,则队列大小将设置为 5。
my $sock = IO::Socket->new(..., Timeout => 5);
此套接字连接的超时值(以秒为单位)。如何利用此值在利用该值的套接字域子类中定义。
my $sock = IO::Socket->new(..., Type => IO::Socket::SOCK_STREAM);
将使用的套接字类型。这些通常是 SOCK_STREAM
、SOCK_DGRAM
或 SOCK_RAW
。如果此参数未定义,则将尝试从服务名称推断类型。
例如,通常会将 SOCK_STREAM
与 tcp
连接一起使用,将 SOCK_DGRAM
与 udp
连接一起使用。
IO::Socket
扩展了 IO::Handle 构造函数。
my $sock = IO::Socket->new();
# get a new IO::Socket::INET instance
$sock = IO::Socket->new(Domain => IO::Socket::AF_INET);
# get a new IO::Socket::UNIX instance
$sock = IO::Socket->new(Domain => IO::Socket::AF_UNIX);
# Domain is the only required argument
$sock = IO::Socket->new(
Domain => IO::Socket::AF_INET, # AF_INET, AF_UNIX
Type => IO::Socket::SOCK_STREAM, # SOCK_STREAM, SOCK_DGRAM, ...
Proto => 'tcp', # 'tcp', 'udp', IPPROTO_TCP, IPPROTO_UDP
# and so on...
);
创建一个 IO::Socket
,它是一个新创建符号的引用(参见 Symbol 包)。new
可选地获取参数,这些参数在 "CONSTRUCTOR ARGUMENTS" in IO::Socket 中定义。
任何 "CONSTRUCTOR ARGUMENTS" in IO::Socket 都可以传递给构造函数,但如果提供了任何参数,则其中一个必须是 "Domain" in IO::Socket 参数。默认情况下,"Domain" in IO::Socket 参数可以是 AF_INET
或 AF_UNIX
。如果注册了域系列的适当子类,则可以使用其他域。所有其他参数将传递给该域包的 configuration
方法。
如果构造函数失败,它将返回 undef
并设置 $errstr
包变量以包含错误消息。
$sock = IO::Socket->new(...)
or die "Cannot create socket - $IO::Socket::errstr\n";
出于传统原因,错误消息也设置到全局 $@
变量中,你可能仍然会发现旧代码在这里查找。
$sock = IO::Socket->new(...)
or die "Cannot create socket - $@\n";
IO::Socket
继承 IO::Handle 中的所有方法并实现以下新方法。
my $client_sock = $sock->accept();
my $inet_sock = $sock->accept('IO::Socket::INET');
accept 方法将在套接字上执行系统调用 accept
并返回一个新对象。新对象将在与侦听套接字相同的类中创建,除非指定了特定的包名称。此对象可用于与尝试连接的客户端通信。
这与 perlfunc 中的 accept
函数略有不同。
在标量上下文中,将返回新套接字,或者在失败时返回 undef
。在列表上下文中,将返回一个包含新套接字和对等地址的二元数组;失败时列表将为空。
my $integer = $sock->atmark();
# read in some data on a given socket
my $data;
$sock->read($data, 1024) until $sock->atmark;
# or, export the function to use:
use IO::Socket 'sockatmark';
$sock->read($data, 1024) until sockatmark($sock);
如果套接字当前位于紧急数据标记处,则为真,否则为假。如果你的系统尚未实现 sockatmark
,这将引发异常。
如果你的系统不支持 sockatmark
,则 use
声明将在编译时失败。
# by default, autoflush will be turned on when referenced
$sock->autoflush(); # turns on autoflush
# turn off autoflush
$sock->autoflush(0);
# turn on autoflush
$sock->autoflush(1);
此属性不会覆盖 IO::Handle 的实现。但是,由于我们默认启用它,因此值得在此处提及。
use Socket qw(pack_sockaddr_in);
my $port = 3000;
my $ip_address = '0.0.0.0';
my $packed_addr = pack_sockaddr_in($port, $ip_address);
$sock->bind($packed_addr);
将网络地址绑定到套接字,就像 bind(2)
所做的那样。如果成功,则返回 true,否则返回 false。您应该为套接字提供适当类型的打包地址。
my $peer_addr = $sock->connected();
if ($peer_addr) {
say "We're connected to $peer_addr";
}
如果套接字处于连接状态,则返回对等地址。如果套接字未处于连接状态,则返回 undef
。
请注意,此方法认为半开 TCP 套接字“处于连接状态”。具体来说,它不区分 ESTABLISHED 和 CLOSE-WAIT TCP 状态;它在任何情况下都返回对等地址,而不是 undef
。因此,通常情况下,它不能可靠地判断对等方是否已启动正常关闭,因为在大多数情况下(见下文),本地 TCP 状态机在本地应用程序调用 IO::Socket 中的“shutdown” 或 close
之前仍处于 CLOSE-WAIT 状态。仅在这一点上,此函数才返回 undef
。
之所以说“在大多数情况下”,是因为本地 TCP 状态机行为可能取决于对等方的套接字选项。特别是,如果对等方套接字启用了 SO_LINGER
且超时为零,则对等方的 close
将生成 RST
段。在收到该段后,本地 TCP 立即转换为 CLOSED,在此状态下,此方法将返回 undef
。
my $value = $sock->getsockopt(SOL_SOCKET, SO_REUSEADDR);
my $buf = $socket->getsockopt(SOL_SOCKET, SO_RCVBUF);
say "Receive buffer is $buf bytes";
获取与套接字关联的选项。此处可以指定除 SOL_SOCKET
之外的级别。为了方便起见,此方法会将正确大小的字节缓冲区解包回数字。
$sock->listen(5);
执行与 listen(2)
系统调用相同的操作。如果成功,则返回 true,否则返回 false。使用给定的队列大小侦听套接字。
my $sockaddr_in = $sock->peername();
返回套接字连接另一端的打包 sockaddr
地址。它调用 getpeername
。
my $proto = $sock->protocol();
如果已知,则返回套接字上使用的协议的编号。如果协议未知,如 AF_UNIX
套接字,则返回零。
my $buffer = "";
my $length = 1024;
my $flags = 0; # default. optional
$sock->recv($buffer, $length);
$sock->recv($buffer, $length, $flags);
功能类似于 perlfunc 中的“recv”。
在套接字上接收消息。尝试从指定的套接字接收 $length
个字符的数据到 $buffer
中。$buffer
将增长或缩小到实际读取的长度。采用与同名系统调用相同的标志。如果套接字的协议支持,则返回发件人的地址;否则返回空字符串。如果出现错误,则返回 undef
。此调用实际上是根据 recvfrom(2)
系统调用实现的。
标志是 ORed 在一起的值,例如 MSG_BCAST
、MSG_OOB
、MSG_TRUNC
。标志的默认值为 0
。
recv
的结果会更新 IO::Socket 中 peername 的缓存值。
注意:在 Perl v5.30 及更新版本中,如果套接字已标记为 :utf8
,recv
将抛出异常。:encoding(...)
层隐式引入了 :utf8
层。请参见 perlfunc 中的 binmode。
注意:在低于 v5.30 的 Perl 版本中,根据套接字的状态,将接收(8 位)字节或字符。默认情况下,所有套接字都对字节进行操作,但例如,如果已使用 perlfunc 中的 binmode 更改套接字以使用 :encoding(UTF-8)
I/O 层(请参见 perlfunc 中的 open 实用程序),则 I/O 将对 UTF8 编码的 Unicode 字符进行操作,而不是字节。:encoding
层类似:在这种情况下,几乎可以读取任何字符。
my $message = "Hello, world!";
my $flags = 0; # defaults to zero
my $to = '0.0.0.0'; # optional destination
my $sent = $sock->send($message);
$sent = $sock->send($message, $flags);
$sent = $sock->send($message, $flags, $to);
在套接字上发送消息。尝试将标量消息发送到套接字。采用与同名系统调用的相同标志。对于未连接的套接字,您必须指定要发送到的目标,在这种情况下,它会执行 sendto(2)
系统调用。返回发送的字符数,或在出错时返回 undef
。目前尚未实现 sendmsg(2)
系统调用。
flags
选项是可选的,默认为 0
。
在使用 $to
成功发送后,在未连接的套接字上对 send
的进一步调用在没有 $to
的情况下将发送到同一地址,并且 $to
将用作 IO::Socket 中 peername 的结果。
注意:在 Perl v5.30 及更新版本中,如果套接字已标记为 :utf8
,send
将抛出异常。:encoding(...)
层隐式引入了 :utf8
层。请参见 perlfunc 中的 binmode。
注意:在低于 v5.30 的 Perl 版本中,根据套接字的状态,将发送(8 位)字节或字符。默认情况下,所有套接字都对字节进行操作,但例如,如果已使用 perlfunc 中的 binmode 更改套接字以使用 :encoding(UTF-8)
I/O 层(请参见 perlfunc 中的 open 实用程序),则 I/O 将对 UTF8 编码的 Unicode 字符进行操作,而不是字节。:encoding
层类似:在这种情况下,几乎可以发送任何字符。
$sock->setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
$sock->setsockopt(SOL_SOCKET, SO_RCVBUF, 64*1024);
设置与套接字关联的选项。此处可以指定除 SOL_SOCKET
以外的级别。为了方便,此方法会将数字转换为打包的字节缓冲区。
$sock->shutdown(SHUT_RD); # we stopped reading data
$sock->shutdown(SHUT_WR); # we stopped writing data
$sock->shutdown(SHUT_RDWR); # we stopped using this socket
按照传入的值指示的方式关闭套接字连接,该值与同名系统调用的解释相同。
当您想告诉另一方您已完成写入但未完成读取,或反之亦然时,此方法对于套接字很有用。它也是 close
的一种更坚决的形式,因为它还会禁用其他进程中任何已 fork 副本中的文件描述符。
成功时返回 1
;如果套接字不是有效的文件句柄,则在出错时返回 undef
,或返回 0
并为任何其他故障设置 $!
。
my $domain = $sock->sockdomain();
返回套接字域类型的数字。例如,对于 AF_INET
套接字,将返回 &AF_INET
的值。
my $sock = IO::Socket->new(); # no values given
# now let's actually get a socket with the socket method
# domain, type, and protocol are required
$sock = $sock->socket(AF_INET, SOCK_STREAM, 'tcp');
打开指定类型的套接字并返回它。域、类型和协议的指定方式与同名系统调用的相同。
my ($r, $w) = $sock->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
($r, $w) = IO::Socket::UNIX
->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
将返回一个列表,其中包含两个已创建的套接字(读取和写入),或在失败时返回一个空列表。
与 perlfunc 中的 socketpair
略有不同,因为参数列表更简单一些。
my $packed_addr = $sock->sockname();
返回此连接端点的打包 sockaddr
地址。它与 getsockname(2)
相同。
my $value = $sock->sockopt(SO_REUSEADDR);
$sock->sockopt(SO_REUSEADDR, 1);
统一的方法,用于同时在 SOL_SOCKET
级别设置和获取选项。如果使用一个参数调用,则会调用 IO::Socket 中的“getsockopt”,否则会调用 IO::Socket 中的“setsockopt”。
my $type = $sock->socktype();
返回套接字类型的数字。例如,对于 SOCK_STREAM
套接字,将返回 &SOCK_STREAM
的值。
my $seconds = $sock->timeout();
my $old_val = $sock->timeout(5); # set new and return old value
设置或获取与此套接字关联的超时值(以秒为单位)。如果未提供任何参数进行调用,则返回当前设置。如果使用参数进行调用,则会更改当前设置并返回前一个值。
此方法可用于所有 IO::Socket
实现,但各个域子类可能使用或不使用此方法。
让我们在 localhost:3333
上创建一个 TCP 服务器。
use strict;
use warnings;
use feature 'say';
use IO::Socket qw(AF_INET AF_UNIX SOCK_STREAM SHUT_WR);
my $server = IO::Socket->new(
Domain => AF_INET,
Type => SOCK_STREAM,
Proto => 'tcp',
LocalHost => '0.0.0.0',
LocalPort => 3333,
ReusePort => 1,
Listen => 5,
) || die "Can't open socket: $IO::Socket::errstr";
say "Waiting on 3333";
while (1) {
# waiting for a new client connection
my $client = $server->accept();
# get information about a newly connected client
my $client_address = $client->peerhost();
my $client_port = $client->peerport();
say "Connection from $client_address:$client_port";
# read up to 1024 characters from the connected client
my $data = "";
$client->recv($data, 1024);
say "received data: $data";
# write response data to the connected client
$data = "ok";
$client->send($data);
# notify client that response has been sent
$client->shutdown(SHUT_WR);
}
$server->close();
这样的服务器的客户端可以是
use strict;
use warnings;
use feature 'say';
use IO::Socket qw(AF_INET AF_UNIX SOCK_STREAM SHUT_WR);
my $client = IO::Socket->new(
Domain => AF_INET,
Type => SOCK_STREAM,
proto => 'tcp',
PeerPort => 3333,
PeerHost => '0.0.0.0',
) || die "Can't open socket: $IO::Socket::errstr";
say "Sending Hello World!";
my $size = $client->send("Hello World!");
say "Sent data of length: $size";
$client->shutdown(SHUT_WR);
my $buffer;
$client->recv($buffer, 1024);
say "Got back $buffer";
$client->close();
在某些系统上,对于使用 new_from_fd
创建的 IO::Socket 对象,或者从该对象中使用 "accept" in IO::Socket 创建的 IO::Socket 对象,"protocol" in IO::Socket、"sockdomain" in IO::Socket 和 "socktype" in IO::Socket 方法可能会返回 undef
。
Socket、IO::Handle、IO::Socket::INET、IO::Socket::UNIX、IO::Socket::IP
Graham Barr。atmark() 由 Lincoln Stein 编写。目前由 Perl 5 Porters 维护。请将所有错误报告到 https://github.com/Perl/perl5/issues。
版权所有 (c) 1997-8 Graham Barr <[email protected]>。保留所有权利。本程序是免费软件;您可以在与 Perl 自身相同的条款下重新分发和/或修改它。
atmark() 实现:版权所有 2001,Lincoln Stein <[email protected]>。本模块在与 Perl 自身相同的条款下分发。只要您保留正确的归属,请随时使用、修改和重新分发它。