はい、では4章の続きです。今度はdRubyでのオブジェクトの渡し方です。基本は本の通りに進めますが、「2章.Hello, dRuby.」で使ったReminderクラス(p.16のreminder0.rb)を使って試そうと思います。多分このクラスが一番馴染み深いと思うからね。じゃあはじめます。
dRubyの場合
dRubyではRubyのクラスライブラリMarshalを用いて他のプロセスにオブジェクトを渡します。 Marshalは任意のオブジェクトをバイト列に直列化するdumpメソッドと、直列化されたバイト列からオブジェクトを再現するloadメソッドからなります。 MarshalはRubyでdeep copy(深いコピー)を行うのにも利用できます。
4.1.2 dRubyでは?
とあるので、前回の最後に書いたとおり、dRubyの場合は値渡しということになりそうです。本ではここでMarshalの実験をしています。が、このサイトでは、まずReminderを使った別の実験をしてみます。というわけで、何回か前のエントリでやったように電文を見ます。今回はデータ部の中身までじろじろ見ていきます。TCPパケットは能弁に語る、気がする。
サーバー側は、2章で登場したサーバーをそのまま使います。クライアント側は
require 'drb/drb'
r = DRbObject.new_with_uri('druby://hoge')
r.add('foo')
str = r[1]
とやり、パケットをダンプしました。
まずは
r.add('foo')を見てみます。この送信電文は以下の通りです。色が変わっている部分がTCPのデータ部です。
うーん、さっぱりわかんね。fooとかaddとか断片的にわかりますが、転送されるデータの内容(データ部の内訳)は謎です。仕方ないので送信部分のソースも見ていくことにします。
まず、DRbMessageの送信部分です。
def send_request(stream, ref, msg_id, arg, b) # :nodoc:
ary = []
ary.push(dump(ref.__drbref))
ary.push(dump(msg_id.id2name))
ary.push(dump(arg.length))
arg.each do |e|
ary.push(dump(e))
end
ary.push(dump(b))
stream.write(ary.join(''))
・・・
です。最後につけられるbってのがわからなかったので調べました。これはmethod_missingの引数の一部みたいですが、どうやらブロック(do … end)が渡されたときにはこっちに入るようです。ブロックを渡すようなことを当面はしそうに無いので、今回はあまり深く考えずに置いておきます。
さらに、このメソッドの中で各所で呼ばれているdumpメソッドの内容も見てみます。
def dump(obj, error=false) # :nodoc:
obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped
begin
str = Marshal::dump(obj)
rescue
str = Marshal::dump(make_proxy(obj, error))
end
[str.size].pack('N') + str
end
ここで一番重要な処理は
[str.size].pack('N') + str
ではないかと思われます。strとは、オブジェクトを文字列に符号化したもの(マーシャルデータ)です。ここではそのstrの前に、strのサイズ(バイト数)を固定長のバイナリデータとしてくっつけています。リファレンスを見てみると、pack('N')とは、big endian unsigned 32bitの形式で、バイナリにパックするメソッドです。で、こいつはプラットフォームに非依存なので、エンディアンの変換は気にしなくてもいいということですね。マーシャルデータの前にサイズをつけているのは、どこまでを読んで復元してよいのかわからないからです。可変長のデータを送受信するときの常套手段かな(それか最後に、“ここまで”という記号をつけて送るかどちらか。)
これでデータ部をだいたい理解できるようになったはず。ではデータ部の先頭から順番に見ていきます。ここはオプション引数が入っているはずの部分です。
00 00 00 03 04 08 30
先頭から4バイトが、データ長をpack('N')したものです。03なので、ここから3バイトがオブジェクトの内容だと言う事だと思います。内容は04 08 30ですが、これはnilってことみたいです。実際に以下のようなコードを実行してみると同じ結果になります。
obj = nil
Marshal::dump(obj).each_byte do |b|
puts sprintf("%x",b)
end
今回はオプション引数が無いのでnilのようです。もし「2章.Hello, dRuby (その5)」で試したようにオプション引数を使っていればその文字列が渡されてくるのだと思います。では次。
00 00 00 07 04 08 22 08 61 64 64
先頭4バイトは7なので、それに続く7バイトを見ます。04 08 22 08 61 64 64は'add'をマーシャルデータ化したものです。最後の61 64 64は、ASCIIで'add'ですね。04 08 22 08 はよくわかんないけど、文字列だよ、みたいなことを教えてる情報なんでしょうきっと。
続きも見ていきます。
00 00 00 04 04 08 69 06
これは、数値の1のマーシャルデータです。引数の個数を示します。
00 00 00 07 04 08 22 08 66 6f 6f
次に、上記の個数分だけ引数の内容が続きます。今回は、'foo'という文字列のオブジェクトのマーシャルデータです。(66 6f 6fが'foo'です。)複製されたオブジェクトが渡されています。なので確かに値渡しですね。
ということで、以下がdRubyの(通常の)TCP送信時の電文形式のようです。各々先頭に、サイズを示す4バイトの固定長バイナリが付加されます。
1. オプション引数
2. メソッド(メッセージ)名
3. 引数の数
4. 引数の内容(3の数だけ存在)
5. ブロックの情報
以上です。次回は、サーバーからの応答(メソッドの戻り値)を見ていきたいと思います。
