初めてのRubyネットワークプログラミング(分散とかも)
解析の試験があると言う日にtwitterでぼそっと呟いた発言(http://twitter.com/ranha/statuses/842565499)を、初めてのRubyを読み終わったのでRubyの練習という事で実装してしまいました。
例によって例のごとく適当に動くだけですが、まぁ適当にでも動いているのでそこんとこは宜しくしてください。
require 'curses' require 'drb/drb' require 'pp' sl_width = -1 SL_HEIGHT = 10 class SL attr_accessor :self attr_accessor :next attr_accessor :prev def initialize(uri,targ) puts "Distributed SL Start!" @mutex = Mutex.new @self = 'druby://'+uri+':8888' @status = "initializing" if targ==nil @next = 'druby://'+uri+':8888' @prev = 'druby://'+uri+':8888' else @next = 'druby://'+targ+':8888' @prev = nil end @status = "waiting command" end def get_status() @status end def change_status(state) @mutex.synchronize do @status = state end end def draw_SL() change_status("start sl rendering") Signal.trap(:INT,"SIG_IGN") Curses::init_screen begin sw = Curses::cols sh = Curses::lines x = sw-1 loop do Curses::clear Curses::setpos(0,0) blit(x,sh/2-SL_HEIGHT/2,sw,sh) x-=1 break if x < -sw Curses::refresh sleep 0.01 end ensure Curses::close_screen end change_status("waiting command") Signal.trap(:INT,"DEFAULT") end def start() connect(@next) loop do if @next == @self change_status("start sl") command('Go!') else if @status == "start sl" command('Go!') change_status("waiting command") end sleep 0.1 end end end def command(str) if str=="Go!" draw_SL() change_status("get next") nnode = DRbObject.new_with_uri(@next) change_status("waiting command") nnode.change_status("start sl") end end def connect(targ) puts "Connect "+targ @next = targ nnode = DRbObject.new_with_uri(@next) pnode = DRbObject.new_with_uri(nnode.prev) nnode.change_status("start reconnect") nnode.prev = @self nnode.change_status("waiting command") pnode.change_status("start reconnect") pnode.next = @self @prev = pnode.self pnode.change_status("waiting command") end def getout() puts "escape!" puts "self:"+@self puts "next:"+@next puts "prev:"+@prev nnode = DRbObject.new_with_uri(@next) pnode = DRbObject.new_with_uri(@prev) nnode.change_status("start reconnect") nnode.prev = @prev nnode.change_status("waiting command") pnode.change_status("start reconnect") pnode.next = @next pnode.change_status("waiting command") end end $sl_pattern = Array.new for i in 0..(SL_HEIGHT-1) $sl_pattern[i] = String.new end def check line line.each_byte{|c| if c!=?- && c!=?\n return true end } return false end line = 0 open("sl.pattern"){|file| while l = file.gets if(check l) $sl_pattern[line] << l.chomp line += 1 else line = 0 end end } $sl_width = $sl_pattern.map{|s| s.length}.max def blit x,y,sw,sh for _y in 0..(SL_HEIGHT-1) if x >= 0 Curses::setpos(y+_y,x) if (sw-x)>$sl_width Curses::addstr($sl_pattern[_y]) else Curses::addstr($sl_pattern[_y][0..(sw-x-1)]) end else x<0 Curses::setpos(y+_y,0) Curses::addstr($sl_pattern[_y][-x..-1]) end end end uri = ARGV.shift targ = ARGV.shift mysl = SL.new(uri,targ) DRb.start_service(mysl.self,mysl) begin mysl.start() sleep rescue Interrupt => error mysl.getout() ensure puts "Error owata" exit(0) end
使い方は至って簡単。
一番最初に実行する人 ruby sl.rb 自分のアドレス
二番目以降の人 ruby sl.rb 自分のアドレス 既にこのあほくさいサービスに参加しているアドレス
実例 ruby sl.rb 192.168.10.3 <- 1台目 ruby sl.rb 192.168.10.4 192.168.10.3<- 2台目 ruby sl.rb 192.168.10.5 192.168.10.4<- 3台目(10.4ではなくて、当然10.3でも良い)
別途必要なものとしては、sl.patternというファイルです。
これはslのページから取ってくるか
------------------------------------------------------------------------------- ==== ________ ___________ _D _| |_______/ \__I_I_____===__|_________| |(_)--- | H\________/ | | =|___ ___| / | | H | | | | ||_| |_|| | | | H |__--------------------| [___] | | ________|___H__/__|_____/[][]~\_______| | |/ | |-----------I_____I [][] [] D |=======|__ __/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__ |/-=|___|| || || || |_____/~\___/ \_/ \__/ \__/ \__/ \__/ \_/ ------------------------------------------------------------------------------- _________________ _| \_____A =| | -| | __|________________________|_ |__________________________|_ |_D__D__D_| |_D__D__D_| \_/ \_/ \_/ \_/ -------------------------------------------------------------------------------
これをそのままsl.patternとして保存してあげてください。
終了する時はSLが走っていない時に Ctrl-C です。
自分一人しか参加していない時は延々と走り続けるのでCtrl-\(\ バックスラッシュ)してあげてください。
動作確認は、Ruby1.8.6以上におけるOS X Tiger,LeopardといくつかのLinuxディストリビューションで行いました。
rubyのversionがある程度以上で、sl.patternファイルがちゃんとあれば動くと思います。
開発的な事
はじめてhoge.rb的な物を作ってプログラム書いたかもしれません。
はじめてdistributedなプログラムを書きました。
はじめてネットワークプログラミング的なプログラムを書きました。
drubyは凄い楽だという事が判明。後はもっとrubyらしく書ける気がするんですけど、勢いに任せて数時間で書いたのでまぁこのまんまで。
本当はしゅぽしゅぽ走る感じの方が良いのですが、そこは面倒臭いのでやめました。
さらにうざくする為には、現在見ているターミナル(のタブ)のttyみっけて、/dev/ttyに吐いてしまうという方法があると思うのですが、いかにしてそれをrubyでやるのかとか分からんのでやっていません。
後は自分のIPアドレス入れるとかばかじゃねーのと思うかもしれません。はい確かにそれはそうでifconfigの結果なりを自動で取得しろという話ですが、それも良く分からなかったのでやっていません。
やったのは右から左に文字列出して、ノードの追加と脱落をある程度安全に行っているだけです。
後change_statusがやたら多いのは、本当は中で再帰呼び出し的な事をしていたのですがそれだとthreadが解放されなくて途中で落ちてしまっていたので改善した跡地ということで。