Addrinfo 클래스
Addrinfo 클래스
Addrinfo 클래스는 C의 struct addrinfo를 Ruby로 매핑한 클래스예요. 이 구조체는 인터넷 호스트와 서비스를 식별해요.
출처: Ruby 3.3 API
본문
Public Class Methods
foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block)
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
getaddrinfo(nodename, service, family, socktype, protocol, flags) → [addrinfo, ...]
getaddrinfo(nodename, service, family, socktype, protocol) → [addrinfo, ...]
getaddrinfo(nodename, service, family, socktype) → [addrinfo, ...]
getaddrinfo(nodename, service, family) → [addrinfo, ...]
getaddrinfo(nodename, service) → [addrinfo, ...]
addrinfo 객체 목록을 배열로 반환해요. 이 메서드는 nodename(호스트명)과 service(포트)를 addrinfo로 변환해요. 변환이 유일하지 않으므로 결과는 addrinfo 객체 목록이에요.
변환이 필요 없는 경우 nodename 또는 service는 nil일 수 있어요.
family, socktype, protocol은 선호 프로토콜에 대한 힌트예요. 결과가 SOCK_STREAM 소켓에 사용될 거라면 socktype으로 SOCK_STREAM을 지정해야 해요. 그러면 Addrinfo.getaddrinfo가 SOCK_STREAM에 적합한 addrinfo 목록을 반환해요. 생략하거나 nil을 주면 결과는 제한되지 않아요. 마찬가지로 PF_INET6을 family로 주면 IPv6로 제한돼요.
flags는 다음과 같은 Socket::AI_??? 상수들의 비트 OR이에요. 상수 목록의 정확한 내용은 OS에 따라 달라요.
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(host) → addrinfo
IP 주소에 대한 addrinfo 객체를 반환해요. 결과의 포트, socktype, protocol은 0으로 채워지므로 소켓을 만드는 데는 적합하지 않아요.
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;
}
new(sockaddr) → addrinfo
new(sockaddr, family) → addrinfo
new(sockaddr, family, socktype) → addrinfo
new(sockaddr, family, socktype, protocol) → addrinfo
새 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 인스턴스의 소켓 주소를 구성하는 데 사용돼요. 3번째 요소인 텍스트 호스트명이 nil이 아니면 기록되지만 Addrinfo#inspect에만 사용돼요.
family는 Socket::PF_INET 같은 프로토콜 패밀리를 지정하는 정수예요. PF_ 접두사 유무와 관계없이 :INET, :INET6, :UNIX, "PF_INET" 같은 상수 이름의 심볼 또는 문자열일 수 있어요. 생략하면 PF_UNSPEC으로 가정해요.
socktype은 Socket::SOCK_STREAM 같은 소켓 타입을 지정하는 정수예요. SOCK_ 접두사 유무와 관계없이 :STREAM, :DGRAM, :RAW, "SOCK_STREAM" 같은 상수 이름의 심볼 또는 문자열일 수 있어요. 생략하면 0으로 가정해요.
protocol은 Socket::IPPROTO_TCP 같은 프로토콜을 지정하는 정수예요. family·socktype과 달리 반드시 정수여야 해요. 생략하면 0으로 가정해요. raw 소켓을 제외한 대부분의 프로토콜에서 0은 합리적인 값이에요.
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(host, port) → addrinfo
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(host, port) → addrinfo
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(path [, socktype]) → addrinfo
UNIX 소켓 주소에 대한 addrinfo 객체를 반환해요. socktype은 소켓 타입을 지정하고, 생략하면 :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;
}
Public Instance Methods
afamily → integer
주소 패밀리(address family)를 정수로 반환해요.
Addrinfo.tcp("localhost", 80).afamily == Socket::AF_INET #=> true
bind() { |sock| ... }
self에 바인드된 소켓을 만들어요. 블록이 주어지면 그 블록에 소켓을 전달해 호출하고 블록의 값을 반환해요. 그 외에는 소켓을 반환해요.
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
canonname → string or nil
정식 이름(canonical name)을 문자열로 반환해요. 정식 이름이 없으면 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;
}
connect([opts]) {|socket| ... }
connect([opts])
self의 주소에 연결된 소켓을 만들어요. 선택적 인자 opts는 해시로 표현된 옵션이에요. opts에는 timeout:(초 단위 타임아웃) 옵션이 있을 수 있어요. 블록이 주어지면 그 블록에 소켓을 전달해 호출하고 블록의 값을 반환해요. 그 외에는 소켓을 반환해요.
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
connect_from([local_addr_args], [opts]) {|socket| ... }
connect_from([local_addr_args], [opts])
self의 주소에 연결된 소켓을 만들어요. local_addr_args로 인자가 하나 이상 주어지면 소켓의 로컬 주소로 사용돼요. local_addr_args는 실제 주소를 얻기 위해 family_addrinfo에 주어져요. local_addr_args가 주어지지 않으면 소켓의 로컬 주소는 바인드되지 않아요. 선택적 마지막 인자 opts는 해시 옵션이며 timeout:(초 단위)을 가질 수 있어요. 블록이 주어지면 소켓을 전달해 호출하고 블록의 값을, 그 외에는 소켓을 반환해요.
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
connect_to([remote_addr_args], [opts]) {|socket| ... }
connect_to([remote_addr_args], [opts])
remote_addr_args에 연결되고 self에 바인드된 소켓을 만들어요. 선택적 마지막 인자 opts는 해시 옵션이며 timeout:(초 단위)을 가질 수 있어요. 블록이 주어지면 소켓을 전달해 호출하고 블록의 값을, 그 외에는 소켓을 반환해요.
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
family_addrinfo(*args)
인자로부터 Addrinfo 객체를 만들어요. 인자는 self와 유사하게 해석돼요.
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
getnameinfo → [nodename, service]
getnameinfo(flags) → [nodename, service]
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));
}
inspect → string
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;
}
inspect_sockaddr → string
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));
}
ip? → true or false
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
ip_address → string
IP 주소를 문자열로 반환해요.
Addrinfo.tcp("127.0.0.1", 80).ip_address #=> "127.0.0.1"
Addrinfo.tcp("::1", 80).ip_address #=> "::1"
ip_port → port
포트 번호를 정수로 반환해요.
Addrinfo.tcp("127.0.0.1", 80).ip_port #=> 80
Addrinfo.tcp("::1", 80).ip_port #=> 80
ip_unpack → [addr, 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]
ipv4? → true or false
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
ipv4_loopback?()
IPv4 루프백 주소(127.0.0.0/8)면 true, 그 외에는 false를 반환해요.
ipv4_multicast?()
IPv4 멀티캐스트 주소(224.0.0.0/4)면 true, 그 외에는 false를 반환해요.
ipv4_private?()
IPv4 사설 주소(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)면 true, 그 외에는 false를 반환해요.
ipv6? → true or false
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
ipv6_linklocal?()
IPv6 링크-로컬 주소(fe80::/10)면 true, 그 외에는 false를 반환해요.
ipv6_loopback?()
IPv6 루프백 주소(::1)면 true, 그 외에는 false를 반환해요.
ipv6_mc_global?()
IPv6 멀티캐스트 전역(global) 스코프 주소면 true, 그 외에는 false를 반환해요.
ipv6_mc_linklocal?()
IPv6 멀티캐스트 링크-로컬 스코프 주소면 true, 그 외에는 false를 반환해요.
ipv6_mc_nodelocal?()
IPv6 멀티캐스트 노드-로컬 스코프 주소면 true, 그 외에는 false를 반환해요.
ipv6_mc_orglocal?()
IPv6 멀티캐스트 조직-로컬 스코프 주소면 true, 그 외에는 false를 반환해요.
ipv6_mc_sitelocal?()
IPv6 멀티캐스트 사이트-로컬 스코프 주소면 true, 그 외에는 false를 반환해요.
ipv6_multicast?()
IPv6 멀티캐스트 주소(ff00::/8)면 true, 그 외에는 false를 반환해요.
ipv6_sitelocal?()
IPv6 사이트-로컬 주소(fec0::/10)면 true, 그 외에는 false를 반환해요.
ipv6_to_ipv4()
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
ipv6_unique_local?()
IPv6 유니크 로컬 주소(fc00::/7, RFC4193)면 true, 그 외에는 false를 반환해요.
ipv6_unspecified?()
IPv6 미지정 주소(::)면 true, 그 외에는 false를 반환해요.
ipv6_v4compat?()
IPv4-호환 IPv6 주소(::/80)면 true, 그 외에는 false를 반환해요.
ipv6_v4mapped?()
IPv4-매핑 IPv6 주소(::ffff:0:0/80)면 true, 그 외에는 false를 반환해요.
listen(backlog=Socket::SOMAXCONN) { |sock| ... }
self에 바인드된 수신(listening) 소켓을 만들어요.
# 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
pfamily → integer
프로토콜 패밀리를 정수로 반환해요.
Addrinfo.tcp("localhost", 80).pfamily == Socket::PF_INET #=> true
protocol → integer
프로토콜을 정수로 반환해요.
Addrinfo.tcp("localhost", 80).protocol == Socket::IPPROTO_TCP #=> true
socktype → integer
소켓 타입을 정수로 반환해요.
Addrinfo.tcp("localhost", 80).socktype == Socket::SOCK_STREAM #=> true
to_s → string
소켓 주소를 패킹된 struct sockaddr 문자열로 반환해요.
Addrinfo.tcp("localhost", 80).to_sockaddr
#=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
to_sockaddr → string
소켓 주소를 패킹된 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;
}
unix? → true or false
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
unix_path → path
소켓 경로를 문자열로 반환해요.
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);
}
Protected Instance Methods
connect_internal(local_addrinfo, timeout=nil) { |socket| ... }
local_addrinfo의 주소에 연결된 새 Socket을 만들어요. local_addrinfo가 nil이면 소켓의 주소는 바인드되지 않아요. timeout은 타임아웃 초를 지정하고, 타임아웃이 발생하면 Errno::ETIMEDOUT이 발생해요. 블록이 주어지면 각 주소에 대해 생성된 소켓을 yield 해요.
# 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