Socket 也可能是指函式:socket

內容

名稱

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 "換行" 常數:常數 CRLFCRLF,以及 $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_* 常數。

常數

在以下各群組中,提供的常數可能遠多於在章節標題中給出的範例。如果標題以 ... 結尾,表示可能還有更多;提供的確切常數會取決於編譯時找到的作業系統和標頭。

PF_INET、PF_INET6、PF_UNIX、...

通訊協定家族常數,用於作為 socket() 的第一個引數,或 SO_DOMAINSO_FAMILY 通訊協定選項的值。

AF_INET、AF_INET6、AF_UNIX、...

通訊協定家族常數,由通訊協定地址結構使用,傳遞給 inet_pton() 或 getaddrinfo() 等函式,或由 sockaddr_family() 等函式傳回。

SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、...

通訊協定類型常數,用於作為 socket() 的第二個引數,或 SO_TYPE 通訊協定選項的值。

SOCK_NONBLOCK、SOCK_CLOEXEC

socket(2) 呼叫期間指定 O_NONBLOCKFD_CLOEXEC 旗標的 Linux 專用捷徑。

socket( my $sockh, PF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0 )

SOL_SOCKET

setsockopt() 和 getsockopt() 的通訊協定選項層級常數。

SO_ACCEPTCONN、SO_BROADCAST、SO_ERROR、...

SOL_SOCKET 層級上針對 setsockopt() 和 getsockopt() 的通訊協定選項名稱常數。

IP_OPTIONS、IP_TOS、IP_TTL、...

IPPROTO_IP 層級上針對 IPv4 通訊協定選項的通訊協定選項名稱常數。

IP_PMTUDISC_WANT、IP_PMTUDISC_DONT、...

IP_MTU_DISCOVER 通訊協定選項的通訊協定選項值常數。

IPTOS_LOWDELAY、IPTOS_THROUGHPUT、IPTOS_RELIABILITY、...

IP_TOS 通訊協定選項的通訊協定選項值常數。

MSG_BCAST、MSG_OOB、MSG_TRUNC、...

send() 和 recv() 的訊息旗標常數。

SHUT_RD、SHUT_RDWR、SHUT_WR

shutdown() 的方向常數。

INADDR_ANY、INADDR_BROADCAST、INADDR_LOOPBACK、INADDR_NONE

提供特殊 AF_INET 地址的常數,用於萬用字元、廣播、本機迴路和無效地址。

通常分別等於 inet_aton('0.0.0.0')、inet_aton('255.255.255.255')、inet_aton('localhost') 和 inet_aton('255.255.255.255')。

IPPROTO_IP、IPPROTO_IPV6、IPPROTO_TCP、...

IP 通訊協定常數,用於 socket() 的第三個引數、getsockopt() 或 setsockopt() 的層級引數,或 SO_PROTOCOL socket 選項的值。

TCP_CORK、TCP_KEEPALIVE、TCP_NODELAY、...

IPPROTO_TCP 層級的 TCP socket 選項的 socket 選項名稱常數。

IN6ADDR_ANY、IN6ADDR_LOOPBACK

提供特殊 AF_INET6 地址的常數,用於萬用字元和本機迴路。

通常分別等於 inet_pton(AF_INET6, "::") 和 inet_pton(AF_INET6, "::1")。

IPV6_ADD_MEMBERSHIP、IPV6_MTU、IPV6_V6ONLY、...

IPPROTO_IPV6 層級的 IPv6 socket 選項的 socket 選項名稱常數。

結構處理器

下列函式會在 Perl 值清單和表示結構的封裝二進位字串之間進行轉換。

$family = sockaddr_family $sockaddr

取得封裝的 socket 地址(由 pack_sockaddr_in()、pack_sockaddr_un() 或 Perl 內建函式 getsockname() 和 getpeername() 傳回)。傳回地址家族標籤。這會是 AF_* 常數之一,例如 sockaddr_in 地址的 AF_INETsockaddr_unAF_UNIX。可用於找出要對未知類型的 sockaddr 使用哪個 unpack。

$sockaddr = pack_sockaddr_in $port, $ip_address

接收兩個引數,一個埠號和一個不透明字串(由 inet_aton() 回傳,或一個 v 字串)。回傳一個 sockaddr_in 結構,其中包含這些已封裝的引數,並填入 AF_INET。對於網際網路網域通訊端,這個結構通常是 bind()、connect() 和 send() 中引數所需要的。

未定義的 $port 引數視為零;未定義的 $ip_address 視為致命錯誤。

($port, $ip_address) = unpack_sockaddr_in $sockaddr

接收一個 sockaddr_in 結構(由 pack_sockaddr_in()、getpeername() 或 recv() 回傳)。回傳一個包含兩個元素的清單:埠號和代表 IP 位址的不透明字串(你可以使用 inet_ntoa() 將位址轉換為四點式數字格式)。如果結構不代表 AF_INET 位址,將會 croak。

在純量環境中,將只回傳 IP 位址。

$sockaddr = sockaddr_in $port, $ip_address

($port, $ip_address) = sockaddr_in $sockaddr

pack_sockaddr_in() 或 unpack_sockaddr_in() 的包裝函式。在清單環境中,會解開其引數,並回傳一個包含埠號和 IP 位址的清單。在純量環境中,會將其埠號和 IP 位址引數封裝為 sockaddr_in 並回傳。

主要提供舊版相容性;最好明確使用 pack_sockaddr_in() 或 unpack_sockaddr_in()。

$sockaddr = pack_sockaddr_in6 $port, $ip6_address, [$scope_id, [$flowinfo]]

接收兩個到四個引數,一個埠號、一個不透明字串(由 inet_pton() 回傳)、一個範圍 ID 號碼(選擇性)和一個流量標籤號碼(選擇性)。回傳一個 sockaddr_in6 結構,其中包含這些已封裝的引數,並填入 AF_INET6。pack_sockaddr_in() 的 IPv6 等效函式。

未定義的 $port 引數視為零;未定義的 $ip6_address 視為致命錯誤。

($port, $ip6_address, $scope_id, $flowinfo) = unpack_sockaddr_in6 $sockaddr

接收一個 sockaddr_in6 結構。回傳一個包含四個元素的清單:埠號、代表 IPv6 位址的不透明字串、範圍 ID 和流量標籤。(你可以使用 inet_ntop() 將位址轉換為一般的字串格式)。如果結構不代表 AF_INET6 位址,將會 croak。

在純量環境中,將只回傳 IP 位址。

$sockaddr = sockaddr_in6 $port, $ip6_address, [$scope_id, [$flowinfo]]

($port, $ip6_address, $scope_id, $flowinfo) = sockaddr_in6 $sockaddr

pack_sockaddr_in6() 或 unpack_sockaddr_in6() 的包裝器。在清單內容中,根據 unpack_sockaddr_in6() 解開其參數。在純量內容中,根據 pack_sockaddr_in6() 封裝其參數。

主要提供舊版相容性;最好明確使用 pack_sockaddr_in6() 或 unpack_sockaddr_in6()。

$sockaddr = pack_sockaddr_un $path

採用一個參數,即路徑名稱。傳回已將該路徑封裝在填入的 `AF_UNIX` 中的 `sockaddr_un` 結構。對於 `PF_UNIX` socket,此結構通常是 bind()、connect() 和 send() 中參數所需的結構。

($path) = unpack_sockaddr_un $sockaddr

採用 `sockaddr_un` 結構(由 pack_sockaddr_un()、getpeername() 或 recv() 傳回)。傳回一個元素的清單:路徑名稱。如果結構未代表 `AF_UNIX` 位址,將會 croak。

$sockaddr = sockaddr_un $path

($path) = sockaddr_un $sockaddr

pack_sockaddr_un() 或 unpack_sockaddr_un() 的包裝器。在清單內容中,解開其參數並傳回包含路徑名稱的清單。在純量內容中,將其路徑名稱封裝為 `sockaddr_un` 並傳回。

主要提供舊版相容性;最好明確使用 pack_sockaddr_un() 或 unpack_sockaddr_un()。

僅當您的系統具有 <sys/un.h> 時才支援這些功能。

$ip_mreq = pack_ip_mreq $multiaddr, $interface

採用 IPv4 多播位址和(選擇性)介面位址(或 `INADDR_ANY`)。傳回已將這些參數封裝在其中的 `ip_mreq` 結構。適用於 `IP_ADD_MEMBERSHIP` 和 `IP_DROP_MEMBERSHIP` sockopts。

($multiaddr, $interface) = unpack_ip_mreq $ip_mreq

採用 `ip_mreq` 結構。傳回兩個元素的清單;IPv4 多播位址和介面位址。

$ip_mreq_source = pack_ip_mreq_source $multiaddr, $source, $interface

採用 IPv4 多播位址、來源位址和(選擇性)介面位址(或 `INADDR_ANY`)。傳回已將這些參數封裝在其中的 `ip_mreq_source` 結構。適用於 `IP_ADD_SOURCE_MEMBERSHIP` 和 `IP_DROP_SOURCE_MEMBERSHIP` sockopts。

($multiaddr, $source, $interface) = unpack_ip_mreq_source $ip_mreq

採用 `ip_mreq_source` 結構。傳回三個元素的清單;IPv4 多播位址、來源位址和介面位址。

$ipv6_mreq = pack_ipv6_mreq $multiaddr6, $ifindex

採用 IPv6 多播位址和介面編號。傳回包含已封裝參數的 ipv6_mreq 結構。適用於 IPV6_ADD_MEMBERSHIPIPV6_DROP_MEMBERSHIP sockopts。

($multiaddr6, $ifindex) = unpack_ipv6_mreq $ipv6_mreq

採用 ipv6_mreq 結構。傳回包含兩個元素的清單:IPv6 位址和介面編號。

FUNCTIONS

$ip_address = inet_aton $string

採用提供主機名稱或 IP 位址文字表示形式的字串,並將其轉譯為適合傳遞至 pack_sockaddr_in() 的封裝二進位位址結構。如果傳遞無法解析的主機名稱,則傳回 undef。對於多宿主主機(擁有超過一個位址的主機),將傳回找到的第一個位址。

為確保可攜性,請勿假設 inet_aton() 的結果為 32 位元寬,換句話說,它只會包含網路順序的 IPv4 位址。

這個僅限 IPv4 的函數主要基於舊有原因提供。新編寫的程式碼應改用 getaddrinfo() 或 inet_pton() 以支援 IPv6。

$string = inet_ntoa $ip_address

採用由 unpack_sockaddr_in() 傳回的封裝二進位位址結構(或表示網路順序中 IPv4 位址四個八位元組的 v 字串),並將其轉譯為 d.d.d.d 格式的字串,其中 d 為小於 256 的數字(網際網路位址的正常人類可讀四點表示法)。

這個僅限 IPv4 的函數主要基於舊有原因提供。新編寫的程式碼應改用 getnameinfo() 或 inet_ntop() 以支援 IPv6。

$address = inet_pton $family, $string

採用位址家族(例如 AF_INETAF_INET6)和包含該家族中位址文字表示形式的字串,並將其轉譯為封裝二進位位址結構。

另請參閱 getaddrinfo(),以取得更強大且靈活的函數,用於在提供主機名稱或文字位址的情況下查詢 Socket 位址。

$string = inet_ntop $family, $address

採用位址家族和封裝二進位位址結構,並將其轉譯為位址的人類可讀文字表示形式;通常為 AF_INETd.d.d.d 格式或 AF_INET6hhhh:hhhh::hhhh 格式。

另請參閱 getnameinfo(),這是一個更強大且靈活的功能,可將 Socket 位址轉換為人類可讀的文字表示。

($err, @result) = getaddrinfo $host, $service, [$hints]

給定主機名稱和服務名稱,此函式會嘗試將主機名稱解析為網路位址清單,將服務名稱解析為通訊協定和埠號,然後傳回適合連線() 的位址結構清單。

僅給定主機名稱,此函式會嘗試將其解析為網路位址清單,然後傳回提供這些位址的位址結構清單。

僅給定服務名稱,此函式會嘗試將其解析為通訊協定和埠號,然後傳回適合 bind() 的位址結構清單來表示它。此用法應與 AI_PASSIVE 旗標結合使用;請參閱下方。

如果兩個名稱都沒有給定,它會產生錯誤。

如果存在,$hints 應為雜湊的參考,其中會辨識下列金鑰

flags => INT

包含 AI_* 常數的位元欄位;請參閱下方。

family => INT

限制僅產生此位址家族中的位址

socktype => INT

限制僅產生此 Socket 類型的位址

protocol => INT

限制僅產生此通訊協定的位址

傳回值會是清單;第一個值為錯誤指示,後接位址結構清單(如果沒有發生錯誤)。

錯誤值會是雙變數;可與 EAI_* 錯誤常數相比較,或以人類可讀的錯誤訊息字串列印。如果沒有發生錯誤,它在數字上會是零,在字串上會是空字串。

結果清單中的每個值會是包含下列欄位的雜湊參考

family => INT

位址家族(例如 AF_INET

socktype => INT

Socket 類型(例如 SOCK_STREAM

protocol => INT

通訊協定(例如 IPPROTO_TCP

addr => STRING

封裝字串中的位址(例如 pack_sockaddr_in() 會傳回的位址)

canonname => STRING

如果提供了 AI_CANONNAME 旗標,則為主機的正規名稱,否則為 undef。此欄位只會出現在第一個傳回的位址上。

$hints hash 中識別下列旗標常數。其他旗標常數可能存在,由作業系統提供。

AI_PASSIVE

表示此解析是針對被動 (即傾聽) socket 的 local bind(),而不是主動 (即連線) socket。

AI_CANONNAME

表示呼叫者希望填入結果的正規主機名稱 (canonname) 欄位。

AI_NUMERICHOST

表示呼叫者會傳遞數字位址,而不是主機名稱,且 getaddrinfo() 不得對此名稱執行解析作業。此旗標會防止可能緩慢的網路查詢作業,並在傳遞主機名稱時傳回錯誤。

($err, $hostname, $servicename) = getnameinfo $sockaddr, [$flags, [$xflags]]

給定封裝的 socket 位址 (例如來自 getsockname()、getpeername(),或由 getaddrinfo() 在 addr 欄位中傳回),傳回它所代表的主機名稱和符號服務名稱。$flags 可以是 NI_* 常數的位元遮罩,或在未指定時預設為 0。

傳回值會是清單;第一個值為錯誤狀況,後接主機名稱和服務名稱。

錯誤值會是雙重變數;可與 EAI_* 錯誤常數比較,或列印為人類可讀的錯誤訊息字串。主機和服務名稱會是純粹字串。

下列旗標常數被識別為 $flags。其他旗標常數可能存在,由作業系統提供。

NI_NUMERICHOST

要求直接傳回數字位址的人類可讀字串表示,而不是執行名稱解析作業,該作業可能會將其轉換為主機名稱。這也會避免可能造成阻斷的網路 I/O。

NI_NUMERICSERV

要求直接傳回埠號作為數字表示,而不是執行名稱解析作業,該作業可能會將其轉換為服務名稱。

NI_NAMEREQD

如果名稱解析作業無法提供名稱,則此旗標會導致 getnameinfo() 指出錯誤,而不是將數字表示形式傳回為人類可讀的字串。

NI_DGRAM

指出套接字位址與 SOCK_DGRAM 套接字有關,其名稱在 TCP 和 UDP 協定之間有所不同。

下列常數可用作 $xflags。

NIx_NOHOST

指出呼叫者對結果的主機名稱不感興趣,因此不需要轉換。undef 將傳回為主機名稱。

NIx_NOSERV

指出呼叫者對結果的服務名稱不感興趣,因此不需要轉換。undef 將傳回為服務名稱。

getaddrinfo() / getnameinfo() 錯誤常數

下列常數可能由 getaddrinfo() 或 getnameinfo() 傳回。其他常數可能由作業系統提供。

EAI_AGAIN

名稱解析期間發生暫時性失敗。如果稍後重試,作業可能會成功。

EAI_BADFLAGS

getaddrinfo() 的 flags 提示值或 getnameinfo() 的 $flags 參數包含無法辨識的旗標。

EAI_FAMILY

getaddrinfo() 的 family 提示或傳遞給 getnameinfo() 的套接字位址的類型不受支援。

EAI_NODATA

傳遞給 getaddrinfo() 的主機名稱未提供任何可用的位址資料。

EAI_NONAME

傳遞給 getaddrinfo() 的主機名稱不存在,或傳遞給 getnameinfo() 的位址未與主機名稱關聯,且已傳遞 NI_NAMEREQD 旗標。

EAI_SERVICE

傳遞給 getaddrinfo() 的服務名稱對於 $hints 中指定的套接字類型不可用。

範例

connect() 的查詢

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 地址

若要將主機名稱轉換成人類可讀的純 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_STREAMSOCK_DGRAMSOCK_RAW,導致地址重複輸出三次。getnameinfo() 的 NI_NUMERICHOST 旗標會讓它傳回字串格式的純 IP 地址,而不是反向解析回主機名稱。

此組合執行傳統函式 gethostbyname() 和 inet_ntoa() 的工作。

存取 socket 選項

許多 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 上雙重使用。