Hi, > [1] client B contacts server A (on a well known port), publishes > list of shared files, and _port_ on which it's listening and willing > to serve peer requests > [2] client C contacts server A (on same well known port), requesting > a file. Server discovers a match in B's list, and directs C to B's > listening port. (I don't mean automatic redirection, C needs to > explicitly contact B on its advertised port) > [3] C serves request(s) on published port I wrote using dRuby. ## gossip.rb require 'drb/drb' require 'drb/eq' require 'thread' class Gossip include DRbUndumped def initialize(entry={}) @neighborhood = {} @entry = entry @gossip = {} @mutex = Mutex.new end def regist(who, keys) @mutex.synchronize do @neighborhood[who] = true keys.each do |key| @gossip[key] = [] unless @gossip[key] @gossip[key].push(who) unless @gossip[key].include?(who) end end end def regist_to(who) who.regist(self, @entry.keys) @mutex.synchronize do @neighborhood[who] = true end end def introduce(key) @gossip.fetch(key, []) end def topics (@gossip.keys + @entry.keys).uniq end def [](key) @entry[key] end def walk(who, key, visited) return nil if visited[who] visited[who] = true begin found = who[key] return found if found who.introduce(key).each do |fwd| found = walk(fwd, key, visited) return found if found end rescue # die? end nil end def talk(key) return @entry[key] if @entry.include?(key) visited = {} @neighborhood.keys.each do |who| found = walk(who, key, visited) return found if found end nil end end ## a.rb require 'gossip' uri = ARGV.shift gossip = Gossip.new({}) DRb.start_service(uri, gossip) puts DRb.uri puts '[enter] to exit.' gets ## b.rb require 'gossip' there = ARGV.shift || raise entry = { 'dRuby'=>'too slow.', 'Ruby'=>'snail', 'Hello'=>'World' } b = Gossip.new(entry) DRb.start_service(nil, b) a = DRbObject.new(nil, there) b.regist_to(a) puts DRb.uri puts '[enter] to exit.' gets ## c.rb require 'gossip' there = ARGV.shift || raise DRb.start_service c = Gossip.new a = DRbObject.new(nil, there) c.regist_to(a) p a.topics puts c.talk('Ruby') puts c.talk('dRuby') puts c.talk('eRuby') == How to play? Terminal A % a.rb druby://juke:1234 druby://juke:1234 [enter] to exit. Terminal B % b.rb druby://juke:1234 druby://juke:1238 [enter] to exit. Terminal C % c.rb druby://juke:1234 ["Hello", "Ruby", "dRuby"] snail too slow. nil = File sharing demo ## rbsrc.rb require 'gossip' class Entry def make_entry entry = [] Dir.open('.').each do |fname| entry.push(fname) if /\.rb$/ =~ fname end entry end def initialize @entry = make_entry end def include?(key) @entry.include?(key) end def [](key) return nil unless include?(key) File.open(key) do |fp| puts "Read: [#{key}]" return fp.read end nil end def keys @entry end end if __FILE__ == $0 begin if ARGV[0] == '-s' neighbor_uri = nil ARGV.shift uri = ARGV.shift || raise else neighbor_uri = ARGV.shift || raise uri = nil end rescue puts "usage: #{$0} [-s] <uri> " exit end entry = Entry.new gossip = Gossip.new(entry) DRb.start_service(uri, gossip) if neighbor_uri there = DRbObject.new(nil, neighbor_uri) gossip.regist_to(there) end puts "URI: #{DRb.uri}" puts "query?>" while s = gets puts gossip.talk(s.chomp) p gossip.topics puts "query?>" end end == How to play? * start central server % ruby rbsrc.rb -s druby://juke:1234 * leaf client/server % ruby rbsrc.rb druby://juke:1234 SeKi.