1
0
Fork 0
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:
akr 2009-02-03 07:25:57 +00:00
parent 260d0d0399
commit 24eafe8376
3 changed files with 110 additions and 20 deletions

View file

@ -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.

View file

@ -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

View file

@ -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)