mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new
private function for allocating same port both IPv4 and IPv6. (Socket.tcp_server_sockets): use tcp_server_sockets_port0 for port 0. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@22007 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
260d0d0399
commit
24eafe8376
3 changed files with 110 additions and 20 deletions
|
@ -1,3 +1,9 @@
|
|||
Tue Feb 3 16:23:16 2009 Tanaka Akira <akr@fsij.org>
|
||||
|
||||
* ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new
|
||||
private function for allocating same port both IPv4 and IPv6.
|
||||
(Socket.tcp_server_sockets): use tcp_server_sockets_port0 for port 0.
|
||||
|
||||
Tue Feb 3 14:12:10 2009 Shugo Maeda <shugo@ruby-lang.org>
|
||||
|
||||
* lib/net/imap.rb: validate data before sending to a server.
|
||||
|
|
|
@ -226,11 +226,50 @@ class Socket
|
|||
end
|
||||
end
|
||||
|
||||
def self.tcp_server_sockets_port0(host)
|
||||
ai_list = AddrInfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE)
|
||||
begin
|
||||
sockets = []
|
||||
port = nil
|
||||
ai_list.each {|ai|
|
||||
s = Socket.new(ai.pfamily, ai.socktype, ai.protocol)
|
||||
sockets << s
|
||||
s.ipv6only! if ai.ipv6?
|
||||
s.setsockopt(:SOCKET, :REUSEADDR, 1)
|
||||
if !port
|
||||
s.bind(ai)
|
||||
port = s.local_address.ip_port
|
||||
else
|
||||
s.bind(AddrInfo.tcp(ai.ip_address, port))
|
||||
end
|
||||
s.listen(5)
|
||||
}
|
||||
rescue Errno::EADDRINUSE
|
||||
sockets.each {|s|
|
||||
s.close
|
||||
}
|
||||
retry
|
||||
end
|
||||
sockets
|
||||
ensure
|
||||
if $!
|
||||
sockets.each {|s|
|
||||
s.close if !s.closed?
|
||||
}
|
||||
end
|
||||
end
|
||||
class << self
|
||||
private :tcp_server_sockets_port0
|
||||
end
|
||||
|
||||
# creates TCP server sockets for _host_ and _port_.
|
||||
# _host_ is optional.
|
||||
#
|
||||
# It returns an array of listening sockets.
|
||||
#
|
||||
# If _port_ is 0, actual port number is choosen 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>]
|
||||
|
@ -240,27 +279,36 @@ class Socket
|
|||
# #=> #<AddrInfo: [::]:1296 TCP>
|
||||
# # #<AddrInfo: 0.0.0.0:1296 TCP>
|
||||
#
|
||||
# # IPv6 and IPv4 socket has same port number, 53114, even if it is choosen dynamically.
|
||||
# sockets = Socket.tcp_server_sockets(0)
|
||||
# sockets.each {|s| p s.local_address }
|
||||
# #=> #<AddrInfo: [::]:53114 TCP>
|
||||
# # #<AddrInfo: 0.0.0.0:53114 TCP>
|
||||
#
|
||||
def self.tcp_server_sockets(host=nil, port)
|
||||
last_error = nil
|
||||
sockets = []
|
||||
AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
|
||||
begin
|
||||
s = ai.listen
|
||||
rescue SystemCallError
|
||||
last_error = $!
|
||||
next
|
||||
end
|
||||
sockets << s
|
||||
}
|
||||
if sockets.empty?
|
||||
raise last_error
|
||||
end
|
||||
sockets
|
||||
ensure
|
||||
if $!
|
||||
sockets.each {|s|
|
||||
s.close if !s.closed?
|
||||
return tcp_server_sockets_port0(host) if port == 0
|
||||
begin
|
||||
last_error = nil
|
||||
sockets = []
|
||||
AddrInfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai|
|
||||
begin
|
||||
s = ai.listen
|
||||
rescue SystemCallError
|
||||
last_error = $!
|
||||
next
|
||||
end
|
||||
sockets << s
|
||||
}
|
||||
if sockets.empty?
|
||||
raise last_error
|
||||
end
|
||||
sockets
|
||||
ensure
|
||||
if $!
|
||||
sockets.each {|s|
|
||||
s.close if !s.closed?
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -111,6 +111,21 @@ class TestSocket < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_tcp_server_sockets_port0
|
||||
sockets = Socket.tcp_server_sockets(0)
|
||||
ports = sockets.map {|s| s.local_address.ip_port }
|
||||
the_port = ports.first
|
||||
ports.each {|port|
|
||||
assert_equal(the_port, port)
|
||||
}
|
||||
ensure
|
||||
if sockets
|
||||
sockets.each {|s|
|
||||
s.close
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
if defined? UNIXSocket
|
||||
def test_unix
|
||||
Dir.mktmpdir {|tmpdir|
|
||||
|
@ -144,7 +159,7 @@ class TestSocket < Test::Unit::TestCase
|
|||
}
|
||||
end
|
||||
|
||||
def test_accept_loop
|
||||
def test_accept_loop_with_unix
|
||||
Dir.mktmpdir {|tmpdir|
|
||||
tcp_servers = []
|
||||
clients = []
|
||||
|
@ -171,4 +186,25 @@ class TestSocket < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_accept_loop
|
||||
servers = []
|
||||
begin
|
||||
servers = Socket.tcp_server_sockets(0)
|
||||
port = servers[0].local_address.ip_port
|
||||
Socket.tcp("localhost", port) {|s1|
|
||||
Socket.accept_loop(servers) {|s2, client_ai|
|
||||
begin
|
||||
assert_equal(s1.local_address.ip_unpack, client_ai.ip_unpack)
|
||||
ensure
|
||||
s2.close
|
||||
end
|
||||
break
|
||||
}
|
||||
}
|
||||
ensure
|
||||
servers.each {|s| s.close if !s.closed? }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end if defined?(Socket)
|
||||
|
|
Loading…
Reference in a new issue