ring.rb: specify multicast interface
* lib/rinda/ring.rb (Rinda::RingServer#initialize): accept array arguments of address to specify multicast interface. * lib/rinda/ring.rb (Rinda::RingServer#make_socket): add optional arguments for multicast interface. * test/rinda/test_rinda.rb (TestRingFinger#test_ring_server_ipv4_multicast, TestRingFinger#test_ring_server_ipv6_multicast): add tests for above change. * test/rinda/test_rinda.rb (TestRingServer#test_make_socket_ipv4_multicast, TestRingServer#test_make_socket_ipv6_multicast): change bound interface address because multicast address is not allowed on Linux or Windows. [ruby-core:53692] [Bug #8159] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@40472 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
0cfa2e54a2
commit
93ed9f08ad
20
ChangeLog
20
ChangeLog
@ -1,3 +1,23 @@
|
|||||||
|
Fri Apr 26 00:07:52 2013 Hiroshi Shirosaki <h.shirosaki@gmail.com>
|
||||||
|
|
||||||
|
* lib/rinda/ring.rb (Rinda::RingServer#initialize): accept array
|
||||||
|
arguments of address to specify multicast interface.
|
||||||
|
|
||||||
|
* lib/rinda/ring.rb (Rinda::RingServer#make_socket): add optional
|
||||||
|
arguments for multicast interface.
|
||||||
|
|
||||||
|
* test/rinda/test_rinda.rb
|
||||||
|
(TestRingFinger#test_ring_server_ipv4_multicast,
|
||||||
|
TestRingFinger#test_ring_server_ipv6_multicast): add tests for
|
||||||
|
above change.
|
||||||
|
|
||||||
|
* test/rinda/test_rinda.rb
|
||||||
|
(TestRingServer#test_make_socket_ipv4_multicast,
|
||||||
|
TestRingServer#test_make_socket_ipv6_multicast): change bound
|
||||||
|
interface address because multicast address is not allowed on Linux
|
||||||
|
or Windows.
|
||||||
|
[ruby-core:53692] [Bug #8159]
|
||||||
|
|
||||||
Thu Apr 25 23:45:02 2013 Hiroshi Shirosaki <h.shirosaki@gmail.com>
|
Thu Apr 25 23:45:02 2013 Hiroshi Shirosaki <h.shirosaki@gmail.com>
|
||||||
|
|
||||||
* lib/rinda/ring.rb (Rinda::RingServer#initialize): add a socket
|
* lib/rinda/ring.rb (Rinda::RingServer#initialize): add a socket
|
||||||
|
@ -67,6 +67,29 @@ module Rinda
|
|||||||
# +addresses+ can contain multiple addresses. If a multicast address is
|
# +addresses+ can contain multiple addresses. If a multicast address is
|
||||||
# given in +addresses+ then the RingServer will listen for multicast
|
# given in +addresses+ then the RingServer will listen for multicast
|
||||||
# queries.
|
# queries.
|
||||||
|
#
|
||||||
|
# If you use IPv4 multicast you may need to set an address of the inbound
|
||||||
|
# interface which joins a multicast group.
|
||||||
|
#
|
||||||
|
# ts = Rinda::TupleSpace.new
|
||||||
|
# rs = Rinda::RingServer.new(ts, [['239.0.0.1', '9.5.1.1']])
|
||||||
|
#
|
||||||
|
# You can set addresses as an Array Object. The first element of the
|
||||||
|
# Array is a multicast address and the second is an inbound interface
|
||||||
|
# address. If the second is omitted then '0.0.0.0' is used.
|
||||||
|
#
|
||||||
|
# If you use IPv6 multicast you may need to set both the local interface
|
||||||
|
# address and the inbound interface index:
|
||||||
|
#
|
||||||
|
# rs = Rinda::RingServer.new(ts, [['ff02::1', '::1', 1]])
|
||||||
|
#
|
||||||
|
# The first element is a multicast address and the second is an inbound
|
||||||
|
# interface address. The third is an inbound interface index.
|
||||||
|
#
|
||||||
|
# At this time there is no easy way to get an interface index by name.
|
||||||
|
#
|
||||||
|
# If the second is omitted then '::1' is used.
|
||||||
|
# If the third is omitted then 0 (default interface) is used.
|
||||||
|
|
||||||
def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
|
def initialize(ts, addresses=[Socket::INADDR_ANY], port=Ring_PORT)
|
||||||
@port = port
|
@port = port
|
||||||
@ -80,7 +103,11 @@ module Rinda
|
|||||||
@ts = ts
|
@ts = ts
|
||||||
@sockets = []
|
@sockets = []
|
||||||
addresses.each do |address|
|
addresses.each do |address|
|
||||||
make_socket(address)
|
if Array === address
|
||||||
|
make_socket(*address)
|
||||||
|
else
|
||||||
|
make_socket(address)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@w_services = write_services
|
@w_services = write_services
|
||||||
@ -89,8 +116,20 @@ module Rinda
|
|||||||
|
|
||||||
##
|
##
|
||||||
# Creates a socket at +address+
|
# Creates a socket at +address+
|
||||||
|
#
|
||||||
|
# If +address+ is multicast address then +interface_address+ and
|
||||||
|
# +multicast_interface+ can be set as optional.
|
||||||
|
#
|
||||||
|
# A created socket is bound to +interface_address+. If you use IPv4
|
||||||
|
# multicast then the interface of +interface_address+ is used as the
|
||||||
|
# inbound interface. If +interface_address+ is omitted or nil then
|
||||||
|
# '0.0.0.0' or '::1' is used.
|
||||||
|
#
|
||||||
|
# If you use IPv6 multicast then +multicast_interface+ is used as the
|
||||||
|
# inbound interface. +multicast_interface+ is a network interface index.
|
||||||
|
# If +multicast_interface+ is omitted then 0 (default interface) is used.
|
||||||
|
|
||||||
def make_socket(address)
|
def make_socket(address, interface_address=nil, multicast_interface=0)
|
||||||
addrinfo = Addrinfo.udp(address, @port)
|
addrinfo = Addrinfo.udp(address, @port)
|
||||||
|
|
||||||
socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
|
socket = Socket.new(addrinfo.pfamily, addrinfo.socktype,
|
||||||
@ -105,19 +144,26 @@ module Rinda
|
|||||||
end
|
end
|
||||||
|
|
||||||
if addrinfo.ipv4_multicast? then
|
if addrinfo.ipv4_multicast? then
|
||||||
|
interface_address = '0.0.0.0' if interface_address.nil?
|
||||||
|
socket.bind(Addrinfo.udp(interface_address, @port))
|
||||||
|
|
||||||
mreq = IPAddr.new(addrinfo.ip_address).hton +
|
mreq = IPAddr.new(addrinfo.ip_address).hton +
|
||||||
IPAddr.new('0.0.0.0').hton
|
IPAddr.new(interface_address).hton
|
||||||
|
|
||||||
socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
|
socket.setsockopt(:IPPROTO_IP, :IP_ADD_MEMBERSHIP, mreq)
|
||||||
else
|
else
|
||||||
mreq = IPAddr.new(addrinfo.ip_address).hton + [0].pack('I')
|
interface_address = '::1' if interface_address.nil?
|
||||||
|
socket.bind(Addrinfo.udp(interface_address, @port))
|
||||||
|
|
||||||
|
mreq = IPAddr.new(addrinfo.ip_address).hton +
|
||||||
|
[multicast_interface].pack('I')
|
||||||
|
|
||||||
socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
|
socket.setsockopt(:IPPROTO_IPV6, :IPV6_JOIN_GROUP, mreq)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
socket.bind(addrinfo)
|
||||||
end
|
end
|
||||||
|
|
||||||
socket.bind(addrinfo)
|
|
||||||
|
|
||||||
socket
|
socket
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -575,8 +575,8 @@ class TestRingServer < Test::Unit::TestCase
|
|||||||
assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
|
assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal('239.0.0.1', v4mc.local_address.ip_address)
|
assert_equal('0.0.0.0', v4mc.local_address.ip_address)
|
||||||
assert_equal(@port, v4mc.local_address.ip_port)
|
assert_equal(@port, v4mc.local_address.ip_port)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_make_socket_ipv6_multicast
|
def test_make_socket_ipv6_multicast
|
||||||
@ -595,7 +595,43 @@ class TestRingServer < Test::Unit::TestCase
|
|||||||
assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
|
assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal('ff02::1', v6mc.local_address.ip_address)
|
assert_equal('::1', v6mc.local_address.ip_address)
|
||||||
|
assert_equal(@port, v6mc.local_address.ip_port)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ring_server_ipv4_multicast
|
||||||
|
@rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
|
||||||
|
v4mc = @rs.instance_variable_get('@sockets').first
|
||||||
|
|
||||||
|
if Socket.const_defined?(:SO_REUSEPORT) then
|
||||||
|
assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
|
||||||
|
else
|
||||||
|
assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal('0.0.0.0', v4mc.local_address.ip_address)
|
||||||
|
assert_equal(@port, v4mc.local_address.ip_port)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ring_server_ipv6_multicast
|
||||||
|
skip 'IPv6 not available' unless
|
||||||
|
Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
|
||||||
|
|
||||||
|
begin
|
||||||
|
@rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
|
||||||
|
rescue Errno::EADDRNOTAVAIL
|
||||||
|
return # IPv6 address for multicast not available
|
||||||
|
end
|
||||||
|
|
||||||
|
v6mc = @rs.instance_variable_get('@sockets').first
|
||||||
|
|
||||||
|
if Socket.const_defined?(:SO_REUSEPORT) then
|
||||||
|
assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
|
||||||
|
else
|
||||||
|
assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal('::1', v6mc.local_address.ip_address)
|
||||||
assert_equal(@port, v6mc.local_address.ip_port)
|
assert_equal(@port, v6mc.local_address.ip_port)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user