すがブロ

sugamasaoのhatenablogだよ

パーフェクトRubyから泣く泣く削った標準添付ライブラリ パーフェクトRuby Advent Calendar 2013(12日目)

なんとなく12日が空いていたので書いてみます*1

このエントリは パーフェクトRuby Advent Calendar の12日目のエントリです。 前の日のエントリはイオリさん http://iori-o.github.io/blog/2013/12/11/perfect-ruby-2013-12-11/ でした。

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)

パーフェクトRubyから泣く泣く削った標準添付ライブラリ

ページ数の都合で泣く泣く削ることになってしまった部分というのが多少なりともあります。その他にこれも載せたほうが良いんだけどページ数が(ryというのもあります。

そこで削ってしまったけど、本当はあると良かったなというのを紹介します。

IO#select

これはいわゆるselect(2)を提供してくれます。select(2)って何スカっていうとman select 2すれば良いじゃないって感じなんですけど、たぶんIOオブジェクト(もっと言うとファイルディスクリプタかな)を待ち受けて、読み取り準備ができたオブジェクトを返すって感じですかね。

で?っていう

例えば、TCPServerのサンプルとかで使われてるので、こういう事例を見ると良いと思う。

ここのサンプルソースでTCPServerを使ったエコーサーバがある。

require "socket"

gs = TCPServer.open(0)
socks = [gs]
addr = gs.addr
addr.shift
printf("server is on %s\n", addr.join(":"))

while true
  nsock = select(socks)
  next if nsock == nil
  for s in nsock[0]
    if s == gs
      socks.push(s.accept)
      print(s, " is accepted\n")
    else
      if s.eof?
        print(s, " is gone\n")
        s.close
        socks.delete(s)
      else
        str = s.gets
        s.write(str)
      end
    end
  end
end

これの nsock = select(socks) ってところで使われている。実はKernel#selectはIO#selectと同じなので、単にselectとも書けるのだった。

ここでやってるのはTCPServerをopenして開いたsocketをIO#selectで待って、入力があったらその文字列をそのまま返却する(s.write(str)のところ)というのをやってるわけですね。

ちなみに、上記のスクリプトを実行するとこんな感じで出力がある。

% ruby sample.rb
server is on 51450:0.0.0.0:0.0.0.0

そうしたら、別のターミナルからおもむろに telnet localhost 51450*2 して、何か文字列を打ってみると動作を確認できます。

このIO#selectというのは配列の配列で返ってくるのでなかなかデータ構造を把握するのが大変なのですが、こういう低レイヤーっぽいものの動作を調べるのもなかなか楽しいですよね。

次のアドベンターは id:akiinyo さんです。本書のレビューでも助けて頂きました……!!

*1:11日を書いたイオリさんに指名も受けてしまいましたし

*2:ポート番号は実行のたびに変わる