IO::Socket - 與 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 建立和使用 Socket,而 Socket 提供一個幾乎一對一的介面,用於與 C Socket 函式庫進行通訊。
IO::Socket
是個基底類別,它實際上只定義了所有類型 Socket 共有的那些操作的方法。特定於特定 Socket 網域的操作,其方法定義在 IO::Socket
的子類別中。請參閱 IO::Socket::INET、IO::Socket::UNIX 和 IO::Socket::IP 以了解此類子類別的範例。
IO::Socket
會匯出 Socket 定義的所有函式 (和常數)。
由於 IO::Socket
沒有傳統意義上的屬性,因此可以將下列參數 (而不是屬性) 傳遞到建構函式中。
建構函式參數應傳遞為 Key => 'Value'
對。
唯一需要的參數是 "IO::Socket 中的 Domain"。
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
適用於網際網路地址系列的套接字,並透過 IO::Socket::INET 處理。AF_INET
套接字繫結到網際網路地址和埠。
AF_UNIX
適用於 unix 網域套接字,並透過 IO::Socket::UNIX 處理。AF_UNIX
套接字繫結到檔案系統作為其地址名稱空間。
此參數是必需的。所有其他參數都是選用的。
my $sock = IO::Socket->new(..., Listen => 5);
Listen 應為整數值或保持未設定狀態。
如果提供此參數,此參數會將套接字置於聆聽模式。然後可以使用 "IO::Socket 中的 accept" 方法接受新的連線。所提供的數值用作 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
。如果此參數保持未定義狀態,將嘗試從服務名稱推論類型。
例如,通常會在 tcp
連線中使用 SOCK_STREAM
,在 udp
連線中使用 SOCK_DGRAM
。
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
選擇性地接受參數,這些參數在 "IO::Socket 中的 CONSTRUCTOR ARGUMENTS" 中定義。
任何 IO::Socket 中的「建構函數參數」 都可以傳遞給建構函數,但如果提供任何參數,則其中一個參數必須是 IO::Socket 中的「網域」 參數。預設情況下,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 方法會對 socket 執行 accept
系統呼叫,並傳回一個新物件。新物件將在與監聽 socket 相同的類別中建立,除非指定特定的套件名稱。此物件可用於與嘗試連線的用戶端通訊。
這與 perlfunc 中的 accept
函數略有不同。
在純量內容中,會傳回新的 socket,或在失敗時傳回 undef
。在清單內容中,會傳回包含新的 socket 和對等位址的二元陣列;在失敗時,清單將為空。
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);
如果 socket 目前位於緊急資料標記,則為 True,否則為 False。如果您的系統尚未實作 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);
將網路位址繫結到 socket,就像 bind(2)
所做的一樣。如果成功,則傳回 True,否則傳回 False。您應該提供適合 socket 類型的封裝位址。
my $peer_addr = $sock->connected();
if ($peer_addr) {
say "We're connected to $peer_addr";
}
如果 socket 處於已連線狀態,則會傳回對等端地址。如果 socket 未處於已連線狀態,則會傳回 undef
。
請注意,此方法將半開的 TCP socket 視為「處於已連線狀態」。具體來說,它不會區分 TCP 狀態中的 ESTABLISHED 和 CLOSE-WAIT;它會傳回對等端地址,而不是 undef
。因此,一般來說,它無法用於可靠地得知對等端是否已啟動正常關閉,因為在大部分情況下(見下文),本機 TCP 狀態會保持在 CLOSE-WAIT,直到本機應用程式呼叫 「IO::Socket 中的 shutdown」 或 close
。只有在那個時候,此函式才會傳回 undef
。
「在大部分情況下」的緩衝是因為本機 TCP 狀態行為可能會取決於對等端的 socket 選項。特別是,如果對等端 socket 已啟用 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";
取得與 socket 相關聯的選項。在此處可以指定 SOL_SOCKET
以外的層級。為了方便,此方法會將正確大小的位元組緩衝區解壓縮回數字。
$sock->listen(5);
執行與 listen(2)
系統呼叫相同的工作。如果成功,則傳回 true,否則傳回 false。使用指定的佇列大小傾聽 socket。
my $sockaddr_in = $sock->peername();
傳回 socket 連線另一端的封裝 sockaddr
地址。它會呼叫 getpeername
。
my $proto = $sock->protocol();
如果已知,則傳回 socket 上使用的通訊協定的號碼。如果通訊協定未知(例如 AF_UNIX
socket),則傳回零。
my $buffer = "";
my $length = 1024;
my $flags = 0; # default. optional
$sock->recv($buffer, $length);
$sock->recv($buffer, $length, $flags);
功能類似於 「perlfunc 中的 recv」。
在 socket 上接收訊息。嘗試從指定的 socket 中接收 $length
個字元的資料到 $buffer
中。$buffer
會成長或縮小為實際讀取的長度。採用與同名系統呼叫相同的旗標。如果 socket 的通訊協定支援,則傳回寄件人的地址;否則傳回空字串。如果發生錯誤,則傳回 undef
。此呼叫實際上是根據 recvfrom(2)
系統呼叫來實作的。
旗標是 OR 在一起的值,例如 MSG_BCAST
、MSG_OOB
、MSG_TRUNC
。旗標的預設值為 0
。
recv
的結果會更新 "peername" in IO::Socket 的快取值。
注意:在 Perl v5.30 及更新版本中,如果 socket 已標記為 :utf8
,recv
將會擲回例外。:encoding(...)
層會隱含地引入 :utf8
層。請參閱 "binmode" in perlfunc。
注意:在早於 v5.30 的 Perl 版本中,將會根據 socket 的狀態接收(8 位元)位元組或字元。預設所有 socket 都會在位元組上執行,但例如如果已使用 "binmode" in perlfunc 變更 socket 以使用 :encoding(UTF-8)
I/O 層(請參閱 "open" in perlfunc 實用程式),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);
功能類似於 "send" in perlfunc。
在 socket 上傳送訊息。嘗試將純量訊息傳送至 socket。採用與同名系統呼叫相同的旗標。在未連接的 socket 上,您必須指定要傳送的目的地,這種情況下它會執行 sendto(2)
系統呼叫。傳回已傳送的字元數,或在發生錯誤時傳回 undef
。目前尚未實作 sendmsg(2)
系統呼叫。
flags
選項是選用的,預設為 0
。
在使用 $to
成功傳送後,未連接的 socket 上對 send
的後續呼叫將會傳送至相同的位址,而 $to
將會用作 "peername" in IO::Socket 的結果。
注意:在 Perl v5.30 及更新版本中,如果 socket 已標記為 :utf8
,send
將會擲回例外。:encoding(...)
層會隱含地引入 :utf8
層。請參閱 "binmode" in perlfunc。
注意:在早於 v5.30 的 Perl 版本中,將會根據 socket 的狀態傳送(8 位元)位元組或字元。預設所有 socket 都會在位元組上執行,但例如如果已使用 "binmode" in perlfunc 變更 socket 以使用 :encoding(UTF-8)
I/O 層(請參閱 "open" in perlfunc 實用程式),I/O 將會在 UTF8 編碼的 Unicode 字元上執行,而不是位元組。:encoding
層也是如此:在這種情況下,幾乎可以傳送任何字元。
$sock->setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
$sock->setsockopt(SOL_SOCKET, SO_RCVBUF, 64*1024);
設定與 socket 關聯的選項。除了 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
以傳入值指示的方式關閉 socket 連線,其詮釋與同名系統呼叫相同。
當您想要告訴另一方您已完成寫入但尚未完成讀取,或反之亦然時,這對 socket 很有用。它也是 close
的一種更堅持的形式,因為它也會停用其他程序中任何分岔副本中的檔案描述符。
傳回 1
表示成功;若發生錯誤,則傳回 undef
(如果 socket 不是有效的檔案句柄),或傳回 0
並設定 $!
表示任何其他失敗。
my $domain = $sock->sockdomain();
傳回 socket 領域類型的數字。例如,對於 AF_INET
socket,將傳回 &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');
開啟指定類型的 socket 並傳回它。領域、類型和協定指定方式與同名的系統呼叫相同。
my ($r, $w) = $sock->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
($r, $w) = IO::Socket::UNIX
->socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC);
將傳回清單,其中包含兩個建立的 socket(讀取和寫入),或在失敗時傳回空清單。
與 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();
傳回 socket 類型的數字。例如,對於 SOCK_STREAM
socket,將傳回 &SOCK_STREAM
的值。
my $seconds = $sock->timeout();
my $old_val = $sock->timeout(5); # set new and return old value
設定或取得與此 socket 關聯的逾時值(以秒為單位)。如果未傳入任何引數,則傳回目前的設定。如果傳入引數,則會變更目前的設定,並傳回先前的值。
此方法可供所有 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 物件,或使用 "IO::Socket 中的 accept" 從此類物件建立的物件,"IO::Socket 中的 protocol"、"IO::Socket 中的 sockdomain" 和 "IO::Socket 中的 socktype" 方法可能會傳回 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。
Copyright (c) 1997-8 Graham Barr <gbarr@pobox.com>。保留所有權利。此程式為自由軟體;您可以在與 Perl 相同的條款下重新散布或修改它。
atmark() 實作:Copyright 2001,Lincoln Stein <lstein@cshl.org>。此模組在與 Perl 相同的條款下散布。只要您保留正確的屬性,請隨時使用、修改和重新散布它。