# creates an Addrinfo object from the arguments.
# The arguments are interpreted as similar to 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>
def family_addrinfo(*args)
raise ArgumentError, "no address specified"
elsif Addrinfo === args.first
raise ArgumentError, "too many arguments" if args.length != 1
if (self.pfamily != addrinfo.pfamily) ||
(self.socktype != addrinfo.socktype)
raise ArgumentError, "Addrinfo type mismatch"
raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2
Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0]
raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1
raise ArgumentError, "unexpected family"
# creates a new Socket connected to the address of +local_addrinfo+.
# If _local_addrinfo_ is nil, the address of the socket is not bound.
# The _timeout_ specify the seconds for timeout.
# Errno::ETIMEDOUT is raised when timeout occur.
# If a block is given the created socket is yielded for each address.
def connect_internal(local_addrinfo, timeout=nil) # :yields: socket
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
sock.ipv6only! if self.ipv6?
sock.bind local_addrinfo if local_addrinfo
sock.connect_nonblock(self)
if !IO.select(nil, [sock], nil, timeout)
raise Errno::ETIMEDOUT, 'user specified timeout'
sock.connect_nonblock(self) # check connection failure
sock.close if !sock.closed?
private :connect_internal
# addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... }
# addrinfo.connect_from([local_addr_args], [opts])
# creates a socket connected to the address of self.
# If one or more arguments given as _local_addr_args_,
# it is used as the local address of the socket.
# _local_addr_args_ is given for family_addrinfo to obtain actual address.
# If _local_addr_args_ is not given, the local address of the socket is not bound.
# The optional last argument _opts_ is options represented by a hash.
# _opts_ may have following options:
# [:timeout] specify the timeout in seconds.
# If a block is given, it is called with the socket and the value of the block is returned.
# The socket is returned otherwise.
# 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"
# # 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"
def connect_from(*args, &block)
opts = Hash === args.last ? args.pop : {}
connect_internal(family_addrinfo(*local_addr_args), opts[:timeout], &block)
# addrinfo.connect([opts]) {|socket| ... }
# addrinfo.connect([opts])
# creates a socket connected to the address of self.
# The optional argument _opts_ is options represented by a hash.
# _opts_ may have following options:
# [:timeout] specify the timeout in seconds.
# If a block is given, it is called with the socket and the value of the block is returned.
# The socket is returned otherwise.
# 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"
def connect(opts={}, &block)
connect_internal(nil, opts[:timeout], &block)
# addrinfo.connect_to([remote_addr_args], [opts]) {|socket| ... }
# addrinfo.connect_to([remote_addr_args], [opts])
# creates a socket connected to _remote_addr_args_ and bound to self.
# The optional last argument _opts_ is options represented by a hash.
# _opts_ may have following options:
# [:timeout] specify the timeout in seconds.
# If a block is given, it is called with the socket and the value of the block is returned.
# The socket is returned otherwise.
# 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"
def connect_to(*args, &block)
opts = Hash === args.last ? args.pop : {}
remote_addrinfo = family_addrinfo(*remote_addr_args)
remote_addrinfo.send(:connect_internal, self, opts[:timeout], &block)
# creates a socket bound to self.
# If a block is given, it is called with the socket and the value of the block is returned.
# The socket is returned otherwise.
# Addrinfo.udp("0.0.0.0", 9981).bind {|s|
# s.local_address.connect {|s| s.send "hello", 0 }
# p s.recv(10) #=> "hello"
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
sock.ipv6only! if self.ipv6?
sock.setsockopt(:SOCKET, :REUSEADDR, 1)
sock.close if !sock.closed?
# creates a listening socket bound to self.
def listen(backlog=Socket::SOMAXCONN)
sock = Socket.new(self.pfamily, self.socktype, self.protocol)
sock.ipv6only! if self.ipv6?
sock.setsockopt(:SOCKET, :REUSEADDR, 1)
sock.close if !sock.closed?
# iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo.
# 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)>
def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, &block)
Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags).each(&block)
# Returns an address of the socket suitable for connect in the local machine.
# This method returns _self_.local_address, except following condition.
# - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1).
# - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1).
# If the local address is not suitable for connect, SocketError is raised.
# IPv4 and IPv6 address which port is 0 is not suitable for connect.
# Unix domain socket which has no path is not suitable for connect.
# Addrinfo.tcp("0.0.0.0", 0).listen {|serv|
# p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP>
# serv.connect_address.connect {|c|
# p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>]
if afamily == Socket::AF_INET
raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0
if addr.ip_address == "0.0.0.0"
addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol)
elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6
raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0
if addr.ip_address == "::"
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address.
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address.
addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol)
elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX
raise SocketError, "unbound Unix socket" if addr.unix_path == ""
class Socket < BasicSocket
# enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available.
if defined? Socket::IPV6_V6ONLY
self.setsockopt(:IPV6, :V6ONLY, 1)
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... }
# Socket.tcp(host, port, local_host=nil, local_port=nil, [opts])
# creates a new socket object connected to host:port using TCP/IP.
# If local_host:local_port is given,
# the socket is bound to it.
# The optional last argument _opts_ is options represented by a hash.
# _opts_ may have following options:
# [:connect_timeout] specify the timeout in seconds.
# If a block is given, the block is called with the socket.
# The value of the block is returned.
# The socket is closed when this method returns.
# If no block is given, the socket is returned.
# Socket.tcp("www.ruby-lang.org", 80) {|sock|
# sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n"
def self.tcp(host, port, *rest) # :yield: socket
opts = Hash === rest.last ? rest.pop : {}
raise ArgumentError, "wrong number of arguments (#{rest.length} for 2)" if 2 < rest.length
local_host, local_port = rest
connect_timeout = opts[:connect_timeout]
if local_host != nil || local_port != nil
local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil)
Addrinfo.foreach(host, port, nil, :STREAM) {|ai|
local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily }
ai.connect_from(local_addr, :timeout => connect_timeout) :
ai.connect(:timeout => connect_timeout)
raise SocketError, "no appropriate local address"
ret.close if !ret.closed?
def self.ip_sockets_port0(ai_list, reuseaddr)
s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
s.setsockopt(:SOCKET, :REUSEADDR, 1)
port = s.local_address.ip_port
s.bind(ai.family_addrinfo(ai.ip_address, port))
sockets.each {|s| s.close }
sockets.each {|s| s.close }
private :ip_sockets_port0
def self.tcp_server_sockets_port0(host)
ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
sockets = ip_sockets_port0(ai_list, true)
s.listen(Socket::SOMAXCONN)
sockets.each {|s| s.close }
private :tcp_server_sockets_port0
# creates TCP/IP server sockets for _host_ and _port_.
# it returns an array of listening sockets.
# If a block is given, the block is called with the sockets.
# The value of the block is returned.
# The socket is closed when this method returns.
# If _port_ is 0, actual port number is chosen dynamically.
# However all sockets in the result has same port number.
# # tcp_server_sockets returns two sockets.
# sockets = Socket.tcp_server_sockets(1296)
# p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
# # The sockets contains IPv6 and IPv4 sockets.
# sockets.each {|s| p s.local_address }
# #=> #<Addrinfo: [::]:1296 TCP>
# # #<Addrinfo: 0.0.0.0:1296 TCP>
# # IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically.
# sockets = Socket.tcp_server_sockets(0)
# sockets.each {|s| p s.local_address }
# #=> #<Addrinfo: [::]:53114 TCP>
# # #<Addrinfo: 0.0.0.0:53114 TCP>
# # The block is called with the sockets.
# Socket.tcp_server_sockets(0) {|sockets|
# p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>]
def self.tcp_server_sockets(host=nil, port)
sockets = tcp_server_sockets_port0(host)
Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
sockets.each {|s| s.close }
sockets.each {|s| s.close if !s.closed? }
# yield socket and client address for each a connection accepted via given sockets.
# The arguments are a list of sockets.
# The individual argument should be a socket or an array of sockets.
# This method yields the block sequentially.
# It means that the next connection is not accepted until the block returns.
# So concurrent mechanism, thread for example, should be used to service multiple clients at a time.
def self.accept_loop(*sockets) # :yield: socket, client_addrinfo
raise ArgumentError, "no sockets"