Socket
- 網路常數和支援函數
Socket
是低階模組,由 IO::Socket 模組家族和其他模組使用。以下範例示範一些低階用法,但實際程式可能會使用 IO::Socket
或類似模組提供的較高階 API。
use Socket qw(PF_INET SOCK_STREAM pack_sockaddr_in inet_aton);
socket(my $socket, PF_INET, SOCK_STREAM, 0)
or die "socket: $!";
my $port = getservbyname "echo", "tcp";
connect($socket, pack_sockaddr_in($port, inet_aton("localhost")))
or die "connect: $!";
print $socket "Hello, world!\n";
print <$socket>;
請參閱 "範例" 區段。
此模組提供各種常數、結構處理器和與基於 socket 的網路相關的其他函數。當與 Perl 核心函數(例如 socket()、setsockopt() 和 bind())搭配使用時,提供的數值和函數非常有用。它還提供其他幾個支援函數,主要用於處理網路地址在人類可讀和原生二進位形式之間的轉換,以及主機名稱解析器作業。
此模組預設會匯出一些常數和函數;但為了向後相容性,任何最近新增的符號都不會預設匯出,必須明確要求。當提供匯入清單給 use Socket
行時,預設匯出不會自動匯入。因此,最佳做法是永遠明確列出所有需要的符號。
此外,還提供一些常見的 socket "換行" 常數:常數 CR
、LF
和 CRLF
,以及 $CR
、$LF
和 $CRLF
,分別對應到 \015
、\012
和 \015\012
。如果您不想在程式中使用文字字元,請使用這裡提供的常數。它們不會預設匯出,但可以個別匯入,並使用 :crlf
匯出標籤
use Socket qw(:DEFAULT :crlf);
$sock->print("GET / HTTP/1.0$CRLF");
可以使用標籤 :addrinfo
匯出整個 getaddrinfo() 子系統;這會匯出 getaddrinfo() 和 getnameinfo() 函數,以及所有 AI_*
、NI_*
、NIx_*
和 EAI_*
常數。
在以下各群組中,提供的常數可能遠多於在章節標題中給出的範例。如果標題以 ...
結尾,表示可能還有更多;提供的確切常數會取決於編譯時找到的作業系統和標頭。
通訊協定家族常數,用於作為 socket() 的第一個引數,或 SO_DOMAIN
或 SO_FAMILY
通訊協定選項的值。
通訊協定家族常數,由通訊協定地址結構使用,傳遞給 inet_pton() 或 getaddrinfo() 等函式,或由 sockaddr_family() 等函式傳回。
通訊協定類型常數,用於作為 socket() 的第二個引數,或 SO_TYPE
通訊協定選項的值。
在 socket(2)
呼叫期間指定 O_NONBLOCK
和 FD_CLOEXEC
旗標的 Linux 專用捷徑。
socket( my $sockh, PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0 )
setsockopt() 和 getsockopt() 的通訊協定選項層級常數。
在 SOL_SOCKET
層級上針對 setsockopt() 和 getsockopt() 的通訊協定選項名稱常數。
在 IPPROTO_IP
層級上針對 IPv4 通訊協定選項的通訊協定選項名稱常數。
IP_MTU_DISCOVER
通訊協定選項的通訊協定選項值常數。
IP_TOS
通訊協定選項的通訊協定選項值常數。
send() 和 recv() 的訊息旗標常數。
shutdown() 的方向常數。
提供特殊 AF_INET
地址的常數,用於萬用字元、廣播、本機迴路和無效地址。
通常分別等於 inet_aton('0.0.0.0')、inet_aton('255.255.255.255')、inet_aton('localhost') 和 inet_aton('255.255.255.255')。
IP 通訊協定常數,用於 socket() 的第三個引數、getsockopt() 或 setsockopt() 的層級引數,或 SO_PROTOCOL
socket 選項的值。
IPPROTO_TCP
層級的 TCP socket 選項的 socket 選項名稱常數。
提供特殊 AF_INET6
地址的常數,用於萬用字元和本機迴路。
通常分別等於 inet_pton(AF_INET6, "::") 和 inet_pton(AF_INET6, "::1")。
IPPROTO_IPV6
層級的 IPv6 socket 選項的 socket 選項名稱常數。
下列函式會在 Perl 值清單和表示結構的封裝二進位字串之間進行轉換。
取得封裝的 socket 地址(由 pack_sockaddr_in()、pack_sockaddr_un() 或 Perl 內建函式 getsockname() 和 getpeername() 傳回)。傳回地址家族標籤。這會是 AF_*
常數之一,例如 sockaddr_in
地址的 AF_INET
或 sockaddr_un
的 AF_UNIX
。可用於找出要對未知類型的 sockaddr 使用哪個 unpack。
接收兩個引數,一個埠號和一個不透明字串(由 inet_aton() 回傳,或一個 v 字串)。回傳一個 sockaddr_in
結構,其中包含這些已封裝的引數,並填入 AF_INET
。對於網際網路網域通訊端,這個結構通常是 bind()、connect() 和 send() 中引數所需要的。
未定義的 $port 引數視為零;未定義的 $ip_address 視為致命錯誤。
接收一個 sockaddr_in
結構(由 pack_sockaddr_in()、getpeername() 或 recv() 回傳)。回傳一個包含兩個元素的清單:埠號和代表 IP 位址的不透明字串(你可以使用 inet_ntoa() 將位址轉換為四點式數字格式)。如果結構不代表 AF_INET
位址,將會 croak。
在純量環境中,將只回傳 IP 位址。
pack_sockaddr_in() 或 unpack_sockaddr_in() 的包裝函式。在清單環境中,會解開其引數,並回傳一個包含埠號和 IP 位址的清單。在純量環境中,會將其埠號和 IP 位址引數封裝為 sockaddr_in
並回傳。
主要提供舊版相容性;最好明確使用 pack_sockaddr_in() 或 unpack_sockaddr_in()。
接收兩個到四個引數,一個埠號、一個不透明字串(由 inet_pton() 回傳)、一個範圍 ID 號碼(選擇性)和一個流量標籤號碼(選擇性)。回傳一個 sockaddr_in6
結構,其中包含這些已封裝的引數,並填入 AF_INET6
。pack_sockaddr_in() 的 IPv6 等效函式。
未定義的 $port 引數視為零;未定義的 $ip6_address 視為致命錯誤。
接收一個 sockaddr_in6
結構。回傳一個包含四個元素的清單:埠號、代表 IPv6 位址的不透明字串、範圍 ID 和流量標籤。(你可以使用 inet_ntop() 將位址轉換為一般的字串格式)。如果結構不代表 AF_INET6
位址,將會 croak。
在純量環境中,將只回傳 IP 位址。
pack_sockaddr_in6() 或 unpack_sockaddr_in6() 的包裝器。在清單內容中,根據 unpack_sockaddr_in6() 解開其參數。在純量內容中,根據 pack_sockaddr_in6() 封裝其參數。
主要提供舊版相容性;最好明確使用 pack_sockaddr_in6() 或 unpack_sockaddr_in6()。
採用一個參數,即路徑名稱。傳回已將該路徑封裝在填入的 `AF_UNIX` 中的 `sockaddr_un` 結構。對於 `PF_UNIX` socket,此結構通常是 bind()、connect() 和 send() 中參數所需的結構。
採用 `sockaddr_un` 結構(由 pack_sockaddr_un()、getpeername() 或 recv() 傳回)。傳回一個元素的清單:路徑名稱。如果結構未代表 `AF_UNIX` 位址,將會 croak。
pack_sockaddr_un() 或 unpack_sockaddr_un() 的包裝器。在清單內容中,解開其參數並傳回包含路徑名稱的清單。在純量內容中,將其路徑名稱封裝為 `sockaddr_un` 並傳回。
主要提供舊版相容性;最好明確使用 pack_sockaddr_un() 或 unpack_sockaddr_un()。
僅當您的系統具有 <sys/un.h> 時才支援這些功能。
採用 IPv4 多播位址和(選擇性)介面位址(或 `INADDR_ANY`)。傳回已將這些參數封裝在其中的 `ip_mreq` 結構。適用於 `IP_ADD_MEMBERSHIP` 和 `IP_DROP_MEMBERSHIP` sockopts。
採用 `ip_mreq` 結構。傳回兩個元素的清單;IPv4 多播位址和介面位址。
採用 IPv4 多播位址、來源位址和(選擇性)介面位址(或 `INADDR_ANY`)。傳回已將這些參數封裝在其中的 `ip_mreq_source` 結構。適用於 `IP_ADD_SOURCE_MEMBERSHIP` 和 `IP_DROP_SOURCE_MEMBERSHIP` sockopts。
採用 `ip_mreq_source` 結構。傳回三個元素的清單;IPv4 多播位址、來源位址和介面位址。
採用 IPv6 多播位址和介面編號。傳回包含已封裝參數的 ipv6_mreq
結構。適用於 IPV6_ADD_MEMBERSHIP
和 IPV6_DROP_MEMBERSHIP
sockopts。
採用 ipv6_mreq
結構。傳回包含兩個元素的清單:IPv6 位址和介面編號。
採用提供主機名稱或 IP 位址文字表示形式的字串,並將其轉譯為適合傳遞至 pack_sockaddr_in() 的封裝二進位位址結構。如果傳遞無法解析的主機名稱,則傳回 undef
。對於多宿主主機(擁有超過一個位址的主機),將傳回找到的第一個位址。
為確保可攜性,請勿假設 inet_aton() 的結果為 32 位元寬,換句話說,它只會包含網路順序的 IPv4 位址。
這個僅限 IPv4 的函數主要基於舊有原因提供。新編寫的程式碼應改用 getaddrinfo() 或 inet_pton() 以支援 IPv6。
採用由 unpack_sockaddr_in() 傳回的封裝二進位位址結構(或表示網路順序中 IPv4 位址四個八位元組的 v 字串),並將其轉譯為 d.d.d.d
格式的字串,其中 d
為小於 256 的數字(網際網路位址的正常人類可讀四點表示法)。
這個僅限 IPv4 的函數主要基於舊有原因提供。新編寫的程式碼應改用 getnameinfo() 或 inet_ntop() 以支援 IPv6。
採用位址家族(例如 AF_INET
或 AF_INET6
)和包含該家族中位址文字表示形式的字串,並將其轉譯為封裝二進位位址結構。
另請參閱 getaddrinfo(),以取得更強大且靈活的函數,用於在提供主機名稱或文字位址的情況下查詢 Socket 位址。
採用位址家族和封裝二進位位址結構,並將其轉譯為位址的人類可讀文字表示形式;通常為 AF_INET
的 d.d.d.d
格式或 AF_INET6
的 hhhh:hhhh::hhhh
格式。
另請參閱 getnameinfo(),這是一個更強大且靈活的功能,可將 Socket 位址轉換為人類可讀的文字表示。
給定主機名稱和服務名稱,此函式會嘗試將主機名稱解析為網路位址清單,將服務名稱解析為通訊協定和埠號,然後傳回適合連線() 的位址結構清單。
僅給定主機名稱,此函式會嘗試將其解析為網路位址清單,然後傳回提供這些位址的位址結構清單。
僅給定服務名稱,此函式會嘗試將其解析為通訊協定和埠號,然後傳回適合 bind() 的位址結構清單來表示它。此用法應與 AI_PASSIVE
旗標結合使用;請參閱下方。
如果兩個名稱都沒有給定,它會產生錯誤。
如果存在,$hints 應為雜湊的參考,其中會辨識下列金鑰
包含 AI_*
常數的位元欄位;請參閱下方。
限制僅產生此位址家族中的位址
限制僅產生此 Socket 類型的位址
限制僅產生此通訊協定的位址
傳回值會是清單;第一個值為錯誤指示,後接位址結構清單(如果沒有發生錯誤)。
錯誤值會是雙變數;可與 EAI_*
錯誤常數相比較,或以人類可讀的錯誤訊息字串列印。如果沒有發生錯誤,它在數字上會是零,在字串上會是空字串。
結果清單中的每個值會是包含下列欄位的雜湊參考
位址家族(例如 AF_INET
)
Socket 類型(例如 SOCK_STREAM
)
通訊協定(例如 IPPROTO_TCP
)
封裝字串中的位址(例如 pack_sockaddr_in() 會傳回的位址)
如果提供了 AI_CANONNAME
旗標,則為主機的正規名稱,否則為 undef
。此欄位只會出現在第一個傳回的位址上。
$hints hash 中識別下列旗標常數。其他旗標常數可能存在,由作業系統提供。
表示此解析是針對被動 (即傾聽) socket 的 local bind(),而不是主動 (即連線) socket。
表示呼叫者希望填入結果的正規主機名稱 (canonname
) 欄位。
表示呼叫者會傳遞數字位址,而不是主機名稱,且 getaddrinfo() 不得對此名稱執行解析作業。此旗標會防止可能緩慢的網路查詢作業,並在傳遞主機名稱時傳回錯誤。
給定封裝的 socket 位址 (例如來自 getsockname()、getpeername(),或由 getaddrinfo() 在 addr
欄位中傳回),傳回它所代表的主機名稱和符號服務名稱。$flags 可以是 NI_*
常數的位元遮罩,或在未指定時預設為 0。
傳回值會是清單;第一個值為錯誤狀況,後接主機名稱和服務名稱。
錯誤值會是雙重變數;可與 EAI_*
錯誤常數比較,或列印為人類可讀的錯誤訊息字串。主機和服務名稱會是純粹字串。
下列旗標常數被識別為 $flags。其他旗標常數可能存在,由作業系統提供。
要求直接傳回數字位址的人類可讀字串表示,而不是執行名稱解析作業,該作業可能會將其轉換為主機名稱。這也會避免可能造成阻斷的網路 I/O。
要求直接傳回埠號作為數字表示,而不是執行名稱解析作業,該作業可能會將其轉換為服務名稱。
如果名稱解析作業無法提供名稱,則此旗標會導致 getnameinfo() 指出錯誤,而不是將數字表示形式傳回為人類可讀的字串。
指出套接字位址與 SOCK_DGRAM
套接字有關,其名稱在 TCP 和 UDP 協定之間有所不同。
下列常數可用作 $xflags。
指出呼叫者對結果的主機名稱不感興趣,因此不需要轉換。undef
將傳回為主機名稱。
指出呼叫者對結果的服務名稱不感興趣,因此不需要轉換。undef
將傳回為服務名稱。
下列常數可能由 getaddrinfo() 或 getnameinfo() 傳回。其他常數可能由作業系統提供。
名稱解析期間發生暫時性失敗。如果稍後重試,作業可能會成功。
getaddrinfo() 的 flags
提示值或 getnameinfo() 的 $flags 參數包含無法辨識的旗標。
getaddrinfo() 的 family
提示或傳遞給 getnameinfo() 的套接字位址的類型不受支援。
傳遞給 getaddrinfo() 的主機名稱未提供任何可用的位址資料。
傳遞給 getaddrinfo() 的主機名稱不存在,或傳遞給 getnameinfo() 的位址未與主機名稱關聯,且已傳遞 NI_NAMEREQD
旗標。
傳遞給 getaddrinfo() 的服務名稱對於 $hints 中指定的套接字類型不可用。
getaddrinfo() 函數會將主機名稱和服務名稱轉換成結構清單,每個結構都包含一種潛在方式,可透過該方式連線到指定主機上的指定服務。
use IO::Socket;
use Socket qw(SOCK_STREAM getaddrinfo);
my %hints = (socktype => SOCK_STREAM);
my ($err, @res) = getaddrinfo("localhost", "echo", \%hints);
die "Cannot getaddrinfo - $err" if $err;
my $sock;
foreach my $ai (@res) {
my $candidate = IO::Socket->new();
$candidate->socket($ai->{family}, $ai->{socktype}, $ai->{protocol})
or next;
$candidate->connect($ai->{addr})
or next;
$sock = $candidate;
last;
}
die "Cannot connect to localhost:echo" unless $sock;
$sock->print("Hello, world!\n");
print <$sock>;
由於會傳回潛在候選清單,因此 while
迴圈會輪流嘗試每個候選,直到找到一個同時成功呼叫 socket() 和 connect() 的候選。
此函數執行舊式函數 gethostbyname()、getservbyname()、inet_aton() 和 pack_sockaddr_in() 的工作。
實際上,這個邏輯由 IO::Socket::IP 執行會更好。
getnameinfo() 函式將 socket 地址(例如由 getsockname() 或 getpeername() 傳回)轉換成一對代表地址和服務名稱的人類可讀字串。
use IO::Socket::IP;
use Socket qw(getnameinfo);
my $server = IO::Socket::IP->new(LocalPort => 12345, Listen => 1) or
die "Cannot listen - $@";
my $socket = $server->accept or die "accept: $!";
my ($err, $hostname, $servicename) = getnameinfo($socket->peername);
die "Cannot getnameinfo - $err" if $err;
print "The peer is connected from $hostname\n";
由於在此範例中只使用了主機名稱,因此可以透過傳遞 NIx_NOSERV
旗標來省略將埠號轉換成服務名稱的冗餘轉換。
use Socket qw(getnameinfo NIx_NOSERV);
my ($err, $hostname) = getnameinfo($socket->peername, 0, NIx_NOSERV);
此函式執行傳統函式 unpack_sockaddr_in()、inet_ntoa()、gethostbyaddr() 和 getservbyport() 的工作。
實際上,這個邏輯由 IO::Socket::IP 執行會更好。
若要將主機名稱轉換成人類可讀的純 IP 地址,請使用 getaddrinfo() 將主機名稱轉換成 socket 結構清單,然後對每個結構執行 getnameinfo() 以再次使其成為可讀的 IP 地址。
use Socket qw(:addrinfo SOCK_RAW);
my ($err, @res) = getaddrinfo($hostname, "", {socktype => SOCK_RAW});
die "Cannot getaddrinfo - $err" if $err;
while( my $ai = shift @res ) {
my ($err, $ipaddr) = getnameinfo($ai->{addr}, NI_NUMERICHOST, NIx_NOSERV);
die "Cannot getnameinfo - $err" if $err;
print "$ipaddr\n";
}
getaddrinfo() 的 socktype
提示會篩選結果,僅包含一種 socket 類型和通訊協定。若沒有此提示,大多數作業系統會傳回三個組合,分別是 SOCK_STREAM
、SOCK_DGRAM
和 SOCK_RAW
,導致地址重複輸出三次。getnameinfo() 的 NI_NUMERICHOST
旗標會讓它傳回字串格式的純 IP 地址,而不是反向解析回主機名稱。
此組合執行傳統函式 gethostbyname() 和 inet_ntoa() 的工作。
許多 SO_*
和其他常數提供 socket 選項名稱,以供 getsockopt() 和 setsockopt() 使用。
use IO::Socket::INET;
use Socket qw(SOL_SOCKET SO_RCVBUF IPPROTO_IP IP_TTL);
my $socket = IO::Socket::INET->new(LocalPort => 0, Proto => 'udp')
or die "Cannot create socket: $@";
$socket->setsockopt(SOL_SOCKET, SO_RCVBUF, 64*1024) or
die "setsockopt: $!";
print "Receive buffer is ", $socket->getsockopt(SOL_SOCKET, SO_RCVBUF),
" bytes\n";
print "IP TTL is ", $socket->getsockopt(IPPROTO_IP, IP_TTL), "\n";
為了方便,IO::Socket 的 setsockopt() 方法會將數字轉換成封裝的位元組緩衝區,而 getsockopt() 會將正確大小的位元組緩衝區解封裝回數字。
此模組最初由 Perl 5 Porters 在 Perl 核心維護。
Paul Evans <leonerd@leonerd.org.uk> 在版本 1.95 將其萃取到 CPAN 上雙重使用。