Addrinfo 類別
Addrinfo
類別會將 struct addrinfo
對應到 Ruby。此結構識別網路主機和服務。
公開類別方法
重複執行 Addrinfo.getaddrinfo
取得的 Addrinfo
物件清單。
Addrinfo.foreach(nil, 80) {|x| p x } #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)> # #<Addrinfo: 127.0.0.1:80 UDP (:80)> # #<Addrinfo: [::1]:80 TCP (:80)> # #<Addrinfo: [::1]:80 UDP (:80)>
# File ext/socket/lib/socket.rb, line 230 def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block) Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags, timeout: timeout).each(&block) end
傳回 addrinfo 物件清單作為陣列。
此方法會將 nodename (主機名稱) 和 service (埠) 轉換為 addrinfo。由於轉換並非唯一,因此結果是 addrinfo 物件清單。
如果沒有要轉換的內容,nodename 或 service 可以為 nil。
family、socktype 和 protocol 是首選通訊協定的提示。如果結果將用於具有 SOCK_STREAM 的 socket,應將 socktype 指定為 SOCK_STREAM。如果是這樣,Addrinfo.getaddrinfo
會傳回適用於 SOCK_STREAM 的 addrinfo 清單。如果省略或給定 nil,結果不會受到限制。
類似地,PF_INET6 作為 family 會限制為 IPv6。
flags 應為 Socket::AI_??? 常數的按位元 OR,如下所示。請注意,常數的確切清單取決於作業系統。
AI_PASSIVE Get address to use with bind() AI_CANONNAME Fill in the canonical name AI_NUMERICHOST Prevent host name resolution AI_NUMERICSERV Prevent service name resolution AI_V4MAPPED Accept IPv4-mapped IPv6 addresses AI_ALL Allow all addresses AI_ADDRCONFIG Accept only if any address is assigned
請注意,只要應用程式知道地址的用法,就應指定 socktype。有些平台在省略 socktype 且 servname 指定為整數時會導致錯誤,因為有些埠號(例如 512)在沒有 socktype 的情況下會產生歧義。
Addrinfo.getaddrinfo("www.kame.net", 80, nil, :STREAM) #=> [#<Addrinfo: 203.178.141.194:80 TCP (www.kame.net)>, # #<Addrinfo: [2001:200:dff:fff1:216:3eff:feb1:44d7]:80 TCP (www.kame.net)>]
static VALUE addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self) { VALUE node, service, family, socktype, protocol, flags, opts, timeout; rb_scan_args(argc, argv, "24:", &node, &service, &family, &socktype, &protocol, &flags, &opts); rb_get_kwargs(opts, &id_timeout, 0, 1, &timeout); if (timeout == Qundef) { timeout = Qnil; } return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout); }
傳回 IP 位址的 addrinfo 物件。
結果的 port、socktype、protocol 會填入零。因此,建立 socket 並不適當。
Addrinfo.ip("localhost") #=> #<Addrinfo: 127.0.0.1 (localhost)>
static VALUE addrinfo_s_ip(VALUE self, VALUE host) { VALUE ret; rb_addrinfo_t *rai; ret = addrinfo_firstonly_new(host, Qnil, INT2NUM(PF_UNSPEC), INT2FIX(0), INT2FIX(0), INT2FIX(0)); rai = get_addrinfo(ret); rai->socktype = 0; rai->protocol = 0; return ret; }
傳回 Addrinfo
的新執行個體。執行個體包含 sockaddr、family、socktype、protocol。sockaddr 表示可供 connect(2) 等使用的 struct sockaddr。family、socktype 和 protocol 是用於 socket(2) 引數的整數。
sockaddr 指定為陣列或字串。陣列應與 IPSocket#addr
或 UNIXSocket#addr
的值相容。字串應為 Socket.sockaddr_in
或 Socket.unpack_sockaddr_un
所產生的 struct sockaddr。
sockaddr 範例
-
["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"]
-
["AF_INET6", 42304, "ip6-localhost", "::1"]
-
["AF_UNIX", "/tmp/sock"]
-
Socket.sockaddr_in("smtp", "2001:DB8::1")
-
Socket.sockaddr_in(80, "172.18.22.42")
-
Socket.sockaddr_in(80, "www.ruby-lang.org")
-
Socket.sockaddr_un("/tmp/sock")
在 AF_INET/AF_INET6 sockaddr 陣列中,第 4 個元素(數字 IP 位址)用於在 Addrinfo
執行個體中建構 socket 位址。如果第 3 個元素(文字主機名稱)非 nil,也會記錄它,但僅用於 Addrinfo#inspect
。
family 指定為整數以指定通訊協定系列,例如 Socket::PF_INET。它可以是符號或字串,也就是帶有或不帶有 PF_ 前綴的常數名稱,例如 :INET、:INET6、:UNIX、“PF_INET”等。如果省略,則假設為 PF_UNSPEC。
socktype 指定為整數以指定 socket 類型,例如 Socket::SOCK_STREAM。它可以是符號或字串,也就是常數名稱,加上或不加上 SOCK_ 前綴,例如::STREAM、:DGRAM、:RAW、“SOCK_STREAM” 等。如果省略,則假設為 0。
protocol 指定為整數以指定通訊協定,例如 Socket::IPPROTO_TCP。它必須是整數,與 family 和 socktype 不同。如果省略,則假設為 0。請注意,0 是大多數通訊協定的合理值,原始 socket 除外。
static VALUE addrinfo_initialize(int argc, VALUE *argv, VALUE self) { rb_addrinfo_t *rai; VALUE sockaddr_arg, sockaddr_ary, pfamily, socktype, protocol; int i_pfamily, i_socktype, i_protocol; struct sockaddr *sockaddr_ptr; socklen_t sockaddr_len; VALUE canonname = Qnil, inspectname = Qnil; if (check_addrinfo(self)) rb_raise(rb_eTypeError, "already initialized socket address"); DATA_PTR(self) = rai = alloc_addrinfo(); rb_scan_args(argc, argv, "13", &sockaddr_arg, &pfamily, &socktype, &protocol); i_pfamily = NIL_P(pfamily) ? PF_UNSPEC : rsock_family_arg(pfamily); i_socktype = NIL_P(socktype) ? 0 : rsock_socktype_arg(socktype); i_protocol = NIL_P(protocol) ? 0 : NUM2INT(protocol); sockaddr_ary = rb_check_array_type(sockaddr_arg); if (!NIL_P(sockaddr_ary)) { VALUE afamily = rb_ary_entry(sockaddr_ary, 0); int af; StringValue(afamily); if (rsock_family_to_int(RSTRING_PTR(afamily), RSTRING_LEN(afamily), &af) == -1) rb_raise(rb_eSocket, "unknown address family: %s", StringValueCStr(afamily)); switch (af) { case AF_INET: /* ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"] */ #ifdef INET6 case AF_INET6: /* ["AF_INET6", 42304, "ip6-localhost", "::1"] */ #endif { VALUE service = rb_ary_entry(sockaddr_ary, 1); VALUE nodename = rb_ary_entry(sockaddr_ary, 2); VALUE numericnode = rb_ary_entry(sockaddr_ary, 3); int flags; service = INT2NUM(NUM2INT(service)); if (!NIL_P(nodename)) StringValue(nodename); StringValue(numericnode); flags = AI_NUMERICHOST; #ifdef AI_NUMERICSERV flags |= AI_NUMERICSERV; #endif init_addrinfo_getaddrinfo(rai, numericnode, service, INT2NUM(i_pfamily ? i_pfamily : af), INT2NUM(i_socktype), INT2NUM(i_protocol), INT2NUM(flags), nodename, service); break; } #ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */ { VALUE path = rb_ary_entry(sockaddr_ary, 1); StringValue(path); init_unix_addrinfo(rai, path, SOCK_STREAM); break; } #endif default: rb_raise(rb_eSocket, "unexpected address family"); } } else { StringValue(sockaddr_arg); sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg); sockaddr_len = RSTRING_SOCKLEN(sockaddr_arg); init_addrinfo(rai, sockaddr_ptr, sockaddr_len, i_pfamily, i_socktype, i_protocol, canonname, inspectname); } return self; }
傳回 TCP 位址的 addrinfo 物件。
Addrinfo.tcp("localhost", "smtp") #=> #<Addrinfo: 127.0.0.1:25 TCP (localhost:smtp)>
static VALUE addrinfo_s_tcp(VALUE self, VALUE host, VALUE port) { return addrinfo_firstonly_new(host, port, INT2NUM(PF_UNSPEC), INT2NUM(SOCK_STREAM), INT2NUM(IPPROTO_TCP), INT2FIX(0)); }
傳回 UDP 位址的 addrinfo 物件。
Addrinfo.udp("localhost", "daytime") #=> #<Addrinfo: 127.0.0.1:13 UDP (localhost:daytime)>
static VALUE addrinfo_s_udp(VALUE self, VALUE host, VALUE port) { return addrinfo_firstonly_new(host, port, INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0)); }
傳回 UNIX socket 位址的 addrinfo 物件。
socktype 指定 socket 類型。如果省略,則使用 :STREAM。
Addrinfo.unix("/tmp/sock") #=> #<Addrinfo: /tmp/sock SOCK_STREAM> Addrinfo.unix("/tmp/sock", :DGRAM) #=> #<Addrinfo: /tmp/sock SOCK_DGRAM>
static VALUE addrinfo_s_unix(int argc, VALUE *argv, VALUE self) { VALUE path, vsocktype, addr; int socktype; rb_addrinfo_t *rai; rb_scan_args(argc, argv, "11", &path, &vsocktype); if (NIL_P(vsocktype)) socktype = SOCK_STREAM; else socktype = rsock_socktype_arg(vsocktype); addr = addrinfo_s_allocate(rb_cAddrinfo); DATA_PTR(addr) = rai = alloc_addrinfo(); init_unix_addrinfo(rai, path, socktype); return addr; }
公開實例方法
傳回位址家族為整數。
Addrinfo.tcp("localhost", 80).afamily == Socket::AF_INET #=> true
static VALUE addrinfo_afamily(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return INT2NUM(ai_get_afamily(rai)); }
建立繫結到自我的 socket。
如果提供區塊,則會呼叫區塊並傳入 socket,並傳回區塊的值。否則傳回 socket。
Addrinfo.udp("0.0.0.0", 9981).bind {|s| s.local_address.connect {|s| s.send "hello", 0 } p s.recv(10) #=> "hello" }
# File ext/socket/lib/socket.rb, line 178 def bind sock = Socket.new(self.pfamily, self.socktype, self.protocol) begin sock.ipv6only! if self.ipv6? sock.setsockopt(:SOCKET, :REUSEADDR, 1) sock.bind(self) rescue Exception sock.close raise end if block_given? begin yield sock ensure sock.close end else sock end end
傳回規範名稱為字串。
如果沒有規範名稱,則傳回 nil。
規範名稱由 Addrinfo.getaddrinfo
設定,當指定 AI_CANONNAME 時。
list = Addrinfo.getaddrinfo("www.ruby-lang.org", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME) p list[0] #=> #<Addrinfo: 221.186.184.68:80 TCP carbon.ruby-lang.org (www.ruby-lang.org)> p list[0].canonname #=> "carbon.ruby-lang.org"
static VALUE addrinfo_canonname(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return rai->canonname; }
建立連線到自我位址的 socket。
選用參數 opts 是由雜湊表示的選項。opts 可能有下列選項
- :timeout
-
指定逾時時間(秒)。
如果提供區塊,則會呼叫區塊並傳入 socket,並傳回區塊的值。否則傳回 socket。
Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s| s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" puts s.read }
# File ext/socket/lib/socket.rb, line 140 def connect(timeout: nil, &block) connect_internal(nil, timeout, &block) end
建立連線到自我位址的 socket。
如果一個或多個參數給予 local_addr_args,它會被用作 socket 的本地地址。local_addr_args 給予 family_addrinfo
以取得實際地址。
如果 local_addr_args 未給予,socket 的本地地址不會被綁定。
可選的最後參數 opts 是由 hash 表示的選項。opts 可能有以下選項
- :timeout
-
指定逾時時間(秒)。
如果提供區塊,則會呼叫區塊並傳入 socket,並傳回區塊的值。否則傳回 socket。
Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s| s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" puts s.read } # Addrinfo object can be taken for the argument. Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s| s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" puts s.read }
# File ext/socket/lib/socket.rb, line 117 def connect_from(*args, timeout: nil, &block) connect_internal(family_addrinfo(*args), timeout, &block) end
建立一個連接到 remote_addr_args 並綁定到自己的 socket。
可選的最後參數 opts 是由 hash 表示的選項。opts 可能有以下選項
- :timeout
-
指定逾時時間(秒)。
如果提供區塊,則會呼叫區塊並傳入 socket,並傳回區塊的值。否則傳回 socket。
Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s| s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" puts s.read }
# File ext/socket/lib/socket.rb, line 163 def connect_to(*args, timeout: nil, &block) remote_addrinfo = family_addrinfo(*args) remote_addrinfo.connect_internal(self, timeout, &block) end
從參數建立一個 Addrinfo
物件。
參數的解釋方式與本身類似。
Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80) #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)> Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2") #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM>
# File ext/socket/lib/socket.rb, line 21 def family_addrinfo(*args) if args.empty? raise ArgumentError, "no address specified" elsif Addrinfo === args.first raise ArgumentError, "too many arguments" if args.length != 1 addrinfo = args.first if (self.pfamily != addrinfo.pfamily) || (self.socktype != addrinfo.socktype) raise ArgumentError, "Addrinfo type mismatch" end addrinfo elsif self.ip? raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2 host, port = args Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0] elsif self.unix? raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1 path, = args Addrinfo.unix(path) else raise ArgumentError, "unexpected family" end end
傳回 nodename 和 service 作為一對字串。這會將 addrinfo 中的 struct sockaddr 轉換為文字表示。
flags 應為 Socket::NI_??? 常數的按位元 OR。
Addrinfo.tcp("127.0.0.1", 80).getnameinfo #=> ["localhost", "www"] Addrinfo.tcp("127.0.0.1", 80).getnameinfo(Socket::NI_NUMERICSERV) #=> ["localhost", "80"]
static VALUE addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); VALUE vflags; char hbuf[1024], pbuf[1024]; int flags, error; rb_scan_args(argc, argv, "01", &vflags); flags = NIL_P(vflags) ? 0 : NUM2INT(vflags); if (rai->socktype == SOCK_DGRAM) flags |= NI_DGRAM; error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len, hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf), flags); if (error) { rsock_raise_resolution_error("getnameinfo", error); } return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf)); }
傳回一個以人類可讀形式顯示 addrinfo 的字串。
Addrinfo.tcp("localhost", 80).inspect #=> "#<Addrinfo: 127.0.0.1:80 TCP (localhost)>" Addrinfo.unix("/tmp/sock").inspect #=> "#<Addrinfo: /tmp/sock SOCK_STREAM>"
static VALUE addrinfo_inspect(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int internet_p; VALUE ret; ret = rb_sprintf("#<%s: ", rb_obj_classname(self)); inspect_sockaddr(self, ret); if (rai->pfamily && ai_get_afamily(rai) != rai->pfamily) { ID id = rsock_intern_protocol_family(rai->pfamily); if (id) rb_str_catf(ret, " %s", rb_id2name(id)); else rb_str_catf(ret, " PF_\?\?\?(%d)", rai->pfamily); } internet_p = rai->pfamily == PF_INET; #ifdef INET6 internet_p = internet_p || rai->pfamily == PF_INET6; #endif if (internet_p && rai->socktype == SOCK_STREAM && (rai->protocol == 0 || rai->protocol == IPPROTO_TCP)) { rb_str_cat2(ret, " TCP"); } else if (internet_p && rai->socktype == SOCK_DGRAM && (rai->protocol == 0 || rai->protocol == IPPROTO_UDP)) { rb_str_cat2(ret, " UDP"); } else { if (rai->socktype) { ID id = rsock_intern_socktype(rai->socktype); if (id) rb_str_catf(ret, " %s", rb_id2name(id)); else rb_str_catf(ret, " SOCK_\?\?\?(%d)", rai->socktype); } if (rai->protocol) { if (internet_p) { ID id = rsock_intern_ipproto(rai->protocol); if (id) rb_str_catf(ret, " %s", rb_id2name(id)); else goto unknown_protocol; } else { unknown_protocol: rb_str_catf(ret, " UNKNOWN_PROTOCOL(%d)", rai->protocol); } } } if (!NIL_P(rai->canonname)) { VALUE name = rai->canonname; rb_str_catf(ret, " %s", StringValueCStr(name)); } if (!NIL_P(rai->inspectname)) { VALUE name = rai->inspectname; rb_str_catf(ret, " (%s)", StringValueCStr(name)); } rb_str_buf_cat2(ret, ">"); return ret; }
傳回一個以人類可讀形式顯示 addrinfo 中的 sockaddr 的字串。
Addrinfo.tcp("localhost", 80).inspect_sockaddr #=> "127.0.0.1:80" Addrinfo.tcp("ip6-localhost", 80).inspect_sockaddr #=> "[::1]:80" Addrinfo.unix("/tmp/sock").inspect_sockaddr #=> "/tmp/sock"
VALUE rsock_addrinfo_inspect_sockaddr(VALUE self) { return inspect_sockaddr(self, rb_str_new("", 0)); }
如果 addrinfo 是網際網路(IPv4/IPv6)地址,傳回 true。否則傳回 false。
Addrinfo.tcp("127.0.0.1", 80).ip? #=> true Addrinfo.tcp("::1", 80).ip? #=> true Addrinfo.unix("/tmp/sock").ip? #=> false
static VALUE addrinfo_ip_p(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); return IS_IP_FAMILY(family) ? Qtrue : Qfalse; }
傳回 IP 地址作為字串。
Addrinfo.tcp("127.0.0.1", 80).ip_address #=> "127.0.0.1" Addrinfo.tcp("::1", 80).ip_address #=> "::1"
static VALUE addrinfo_ip_address(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); VALUE vflags; VALUE ret; if (!IS_IP_FAMILY(family)) rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); ret = addrinfo_getnameinfo(1, &vflags, self); return rb_ary_entry(ret, 0); }
傳回埠號作為整數。
Addrinfo.tcp("127.0.0.1", 80).ip_port #=> 80 Addrinfo.tcp("::1", 80).ip_port #=> 80
static VALUE addrinfo_ip_port(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); int port; if (!IS_IP_FAMILY(family)) { bad_family: #ifdef AF_INET6 rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); #else rb_raise(rb_eSocket, "need IPv4 address"); #endif } switch (family) { case AF_INET: if (rai->sockaddr_len != sizeof(struct sockaddr_in)) rb_raise(rb_eSocket, "unexpected sockaddr size for IPv4"); port = ntohs(rai->addr.in.sin_port); break; #ifdef AF_INET6 case AF_INET6: if (rai->sockaddr_len != sizeof(struct sockaddr_in6)) rb_raise(rb_eSocket, "unexpected sockaddr size for IPv6"); port = ntohs(rai->addr.in6.sin6_port); break; #endif default: goto bad_family; } return INT2NUM(port); }
傳回 IP 地址和埠號作為 2 元素陣列。
Addrinfo.tcp("127.0.0.1", 80).ip_unpack #=> ["127.0.0.1", 80] Addrinfo.tcp("::1", 80).ip_unpack #=> ["::1", 80]
static VALUE addrinfo_ip_unpack(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); VALUE vflags; VALUE ret, portstr; if (!IS_IP_FAMILY(family)) rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); ret = addrinfo_getnameinfo(1, &vflags, self); portstr = rb_ary_entry(ret, 1); rb_ary_store(ret, 1, INT2NUM(atoi(StringValueCStr(portstr)))); return ret; }
如果 addrinfo 是 IPv4 地址,傳回 true。否則傳回 false。
Addrinfo.tcp("127.0.0.1", 80).ipv4? #=> true Addrinfo.tcp("::1", 80).ipv4? #=> false Addrinfo.unix("/tmp/sock").ipv4? #=> false
static VALUE addrinfo_ipv4_p(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return ai_get_afamily(rai) == AF_INET ? Qtrue : Qfalse; }
傳回 IPv4 回授位址 (127.0.0.0/8) 的 true。否則傳回 false。
static VALUE addrinfo_ipv4_loopback_p(VALUE self) { uint32_t a; if (!extract_in_addr(self, &a)) return Qfalse; if ((a & 0xff000000) == 0x7f000000) /* 127.0.0.0/8 */ return Qtrue; return Qfalse; }
傳回 IPv4 多播位址 (224.0.0.0/4) 的 true。否則傳回 false。
static VALUE addrinfo_ipv4_multicast_p(VALUE self) { uint32_t a; if (!extract_in_addr(self, &a)) return Qfalse; if ((a & 0xf0000000) == 0xe0000000) /* 224.0.0.0/4 */ return Qtrue; return Qfalse; }
傳回 IPv4 私有位址 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) 的 true。否則傳回 false。
static VALUE addrinfo_ipv4_private_p(VALUE self) { uint32_t a; if (!extract_in_addr(self, &a)) return Qfalse; if ((a & 0xff000000) == 0x0a000000 || /* 10.0.0.0/8 */ (a & 0xfff00000) == 0xac100000 || /* 172.16.0.0/12 */ (a & 0xffff0000) == 0xc0a80000) /* 192.168.0.0/16 */ return Qtrue; return Qfalse; }
如果 addrinfo 是 IPv6 位址,則傳回 true。否則傳回 false。
Addrinfo.tcp("127.0.0.1", 80).ipv6? #=> false Addrinfo.tcp("::1", 80).ipv6? #=> true Addrinfo.unix("/tmp/sock").ipv6? #=> false
static VALUE addrinfo_ipv6_p(VALUE self) { #ifdef AF_INET6 rb_addrinfo_t *rai = get_addrinfo(self); return ai_get_afamily(rai) == AF_INET6 ? Qtrue : Qfalse; #else return Qfalse; #endif }
傳回 IPv6 連結本地位址 (fe80::/10) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_linklocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_LINKLOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 回授位址 (::1) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_loopback_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_LOOPBACK(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播全球範圍位址的 true。否則傳回 false。
static VALUE addrinfo_ipv6_mc_global_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MC_GLOBAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播連結本地範圍位址的 true。否則傳回 false。
static VALUE addrinfo_ipv6_mc_linklocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MC_LINKLOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播節點本地範圍位址的 true。否則傳回 false。
static VALUE addrinfo_ipv6_mc_nodelocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MC_NODELOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播組織本地範圍位址的 true。否則傳回 false。
static VALUE addrinfo_ipv6_mc_orglocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MC_ORGLOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播網站本地範圍位址的 true。否則傳回 false。
static VALUE addrinfo_ipv6_mc_sitelocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MC_SITELOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 多播位址 (ff00::/8) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_multicast_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_MULTICAST(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 網站本機位址 (fec0::/10) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_sitelocal_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_SITELOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv4 已對應/相容 IPv6 位址的 IPv4 位址。如果 self
不是 IPv4 已對應/相容 IPv6 位址,則傳回 nil。
Addrinfo.ip("::192.0.2.3").ipv6_to_ipv4 #=> #<Addrinfo: 192.0.2.3> Addrinfo.ip("::ffff:192.0.2.3").ipv6_to_ipv4 #=> #<Addrinfo: 192.0.2.3> Addrinfo.ip("::1").ipv6_to_ipv4 #=> nil Addrinfo.ip("192.0.2.3").ipv6_to_ipv4 #=> nil Addrinfo.unix("/tmp/sock").ipv6_to_ipv4 #=> nil
static VALUE addrinfo_ipv6_to_ipv4(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); struct in6_addr *addr; int family = ai_get_afamily(rai); if (family != AF_INET6) return Qnil; addr = &rai->addr.in6.sin6_addr; if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) { struct sockaddr_in sin4; INIT_SOCKADDR_IN(&sin4, sizeof(sin4)); memcpy(&sin4.sin_addr, (char*)addr + sizeof(*addr) - sizeof(sin4.sin_addr), sizeof(sin4.sin_addr)); return rsock_addrinfo_new((struct sockaddr *)&sin4, (socklen_t)sizeof(sin4), PF_INET, rai->socktype, rai->protocol, rai->canonname, rai->inspectname); } else { return Qnil; } }
傳回 IPv6 唯一本機位址 (fc00::/7, RFC4193) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_unique_local_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_UNIQUE_LOCAL(addr)) return Qtrue; return Qfalse; }
傳回 IPv6 未指定位址 (::) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_unspecified_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_UNSPECIFIED(addr)) return Qtrue; return Qfalse; }
傳回 IPv4 相容 IPv6 位址 (::/80) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_v4compat_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_V4COMPAT(addr)) return Qtrue; return Qfalse; }
傳回 IPv4 已對應 IPv6 位址 (::ffff:0:0/80) 的 true。否則傳回 false。
static VALUE addrinfo_ipv6_v4mapped_p(VALUE self) { struct in6_addr *addr = extract_in6_addr(self); if (addr && IN6_IS_ADDR_V4MAPPED(addr)) return Qtrue; return Qfalse; }
建立與 self 繫結的監聽 socket。
# File ext/socket/lib/socket.rb, line 200 def listen(backlog=Socket::SOMAXCONN) sock = Socket.new(self.pfamily, self.socktype, self.protocol) begin sock.ipv6only! if self.ipv6? sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX sock.bind(self) sock.listen(backlog) rescue Exception sock.close raise end if block_given? begin yield sock ensure sock.close end else sock end end
傳回協定系列,為整數。
Addrinfo.tcp("localhost", 80).pfamily == Socket::PF_INET #=> true
static VALUE addrinfo_pfamily(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return INT2NUM(rai->pfamily); }
傳回 socket 類型,為整數。
Addrinfo.tcp("localhost", 80).protocol == Socket::IPPROTO_TCP #=> true
static VALUE addrinfo_protocol(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return INT2NUM(rai->protocol); }
傳回 socket 類型,為整數。
Addrinfo.tcp("localhost", 80).socktype == Socket::SOCK_STREAM #=> true
static VALUE addrinfo_socktype(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); return INT2NUM(rai->socktype); }
傳回 socket 位址,為封裝的 struct sockaddr 字串。
Addrinfo.tcp("localhost", 80).to_sockaddr #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
傳回 socket 位址,為封裝的 struct sockaddr 字串。
Addrinfo.tcp("localhost", 80).to_sockaddr #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
static VALUE addrinfo_to_sockaddr(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); VALUE ret; ret = rb_str_new((char*)&rai->addr, rai->sockaddr_len); return ret; }
如果 addrinfo 是 UNIX 位址,則傳回 true。否則傳回 false。
Addrinfo.tcp("127.0.0.1", 80).unix? #=> false Addrinfo.tcp("::1", 80).unix? #=> false Addrinfo.unix("/tmp/sock").unix? #=> true
static VALUE addrinfo_unix_p(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); #ifdef AF_UNIX return ai_get_afamily(rai) == AF_UNIX ? Qtrue : Qfalse; #else return Qfalse; #endif }
傳回 socket 路徑,為字串。
Addrinfo.unix("/tmp/sock").unix_path #=> "/tmp/sock"
static VALUE addrinfo_unix_path(VALUE self) { rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); struct sockaddr_un *addr; long n; if (family != AF_UNIX) rb_raise(rb_eSocket, "need AF_UNIX address"); addr = &rai->addr.un; n = rai_unixsocket_len(rai); if (n < 0) rb_raise(rb_eSocket, "too short AF_UNIX address: %"PRIuSIZE" bytes given for minimum %"PRIuSIZE" bytes.", (size_t)rai->sockaddr_len, offsetof(struct sockaddr_un, sun_path)); if ((long)sizeof(addr->sun_path) < n) rb_raise(rb_eSocket, "too long AF_UNIX path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)", (size_t)n, sizeof(addr->sun_path)); return rb_str_new(addr->sun_path, n); }
受保護的執行個體方法
建立新的 Socket
,連線至 local_addrinfo
的位址。
如果 local_addrinfo 為 nil,則 socket 的位址不會繫結。
timeout 指定逾時秒數。逾時發生時,會引發 Errno::ETIMEDOUT。
如果提供區塊,則會針對每個位址讓建立的 socket 產生區塊。
# File ext/socket/lib/socket.rb, line 54 def connect_internal(local_addrinfo, timeout=nil) # :yields: socket sock = Socket.new(self.pfamily, self.socktype, self.protocol) begin sock.ipv6only! if self.ipv6? sock.bind local_addrinfo if local_addrinfo if timeout case sock.connect_nonblock(self, exception: false) when 0 # success or EISCONN, other errors raise break when :wait_writable sock.wait_writable(timeout) or raise Errno::ETIMEDOUT, 'user specified timeout' end while true else sock.connect(self) end rescue Exception sock.close raise end if block_given? begin yield sock ensure sock.close end else sock end end