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.