[前][次][番号順一覧][スレッド一覧]

ruby-changes:10457

From: akr <ko1@a...>
Date: Tue, 3 Feb 2009 16:26:09 +0900 (JST)
Subject: [ruby-changes:10457] Ruby:r22007 (trunk): * ext/socket/lib/socket.rb (Socket.tcp_server_sockets_port0): new

akr	2009-02-03 16:25:57 +0900 (Tue, 03 Feb 2009)

  New Revision: 22007

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=22007

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/ext/socket/lib/socket.rb
    trunk/test/socket/test_socket.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 22006)
+++ ChangeLog	(revision 22007)
@@ -1,3 +1,9 @@
+Tue Feb  3 16:23:16 2009  Tanaka Akira  <akr@f...>
+
+	* 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@r...>
 
 	* lib/net/imap.rb: validate data before sending to a server.
Index: ext/socket/lib/socket.rb
===================================================================
--- ext/socket/lib/socket.rb	(revision 22006)
+++ ext/socket/lib/socket.rb	(revision 22007)
@@ -226,11 +226,50 @@
     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,28 +279,37 @@
   #   #=> #<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
+    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 << s
-    }
-    if sockets.empty?
-      raise last_error
+      sockets
+    ensure
+      if $!
+        sockets.each {|s|
+          s.close if !s.closed?
+        }
+      end
     end
-    sockets
-  ensure
-    if $!
-      sockets.each {|s|
-        s.close if !s.closed?
-      }
-    end
   end
 
   # yield socket and client address for each a connection accepted via given sockets.
Index: test/socket/test_socket.rb
===================================================================
--- test/socket/test_socket.rb	(revision 22006)
+++ test/socket/test_socket.rb	(revision 22007)
@@ -111,6 +111,21 @@
     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 @@
       }
     end
 
-    def test_accept_loop
+    def test_accept_loop_with_unix
       Dir.mktmpdir {|tmpdir|
         tcp_servers = []
         clients = []
@@ -171,4 +186,25 @@
     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)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]