読者です 読者をやめる 読者になる 読者になる

gen_sctp 試してみた #2

erlang sctp

いいわけ

gen_sctp や RFC やら Draft を読みながら調べていますが、間違っている可能性もあります。メモなので、読めないと思います。

とりあえず

あくまでも gen_sctp を使うことを大前提にしています。

ubuntu
Ubuntu 9.10 a5 server 64bit
apt-get
erlang-base-hipe erlang-eunit erlang-dev
ip
192.168.0.24

One-to-One と One-to-Many について

Draft を読むと、期待していた TCP 型の Accept で待ち受ける One-to-One 型は gen_sctp には実装されておらず、 One-to-Many 型が実装されているようです。

個人的には非同期 accept が使えるなら One-to-One でやりたいのですが、今回はあきらめ。

One-to-Many

これはセッション管理は gen_sctp の Socket がやってくれて、さらにつないできたセッションに対してすべて Association 番号が振られます。これを使う事で簡単にセッションの維持を行うことができるようになります。Socket は一つで accept は自動でやってくれます。このため大量の Socket を管理する必要がなくなるのです。

接続した瞬間に {sctp_assoc_change で comm_up で接続が確立した際に一番最後に入ってくる値が association_id です。

この場合は 2 が association_id となる。

Received: {ok,{{127,0,0,1},54222,[],{sctp_assoc_change,comm_up,0,10,10,2}}}

これを使うことで一つの Socket で複数のクライアント接続を一気に処理できます。

もし接続を突然中断しても shutdown_comp と association_id が上がってきます。

Received: {ok,{{127,0,0,1},39769,[],{sctp_assoc_change,comm_up,0,10,10,2}}}
Received: {ok,{{127,0,0,1},
               39769,
               [{sctp_sndrcvinfo,0,0,[],0,0,0,2529608443,0,2}],
               <<"Test 0">>}}
Received: {ok,{{127,0,0,1},
               39769,[],
               {sctp_paddr_change,{{192,168,0,24},39769},addr_confirmed,2,2}}}
Received: {ok,{{127,0,0,1},39769,[],{sctp_shutdown_event,2}}}
Received: {ok,{{127,0,0,1},
               39769,[],
               {sctp_assoc_change,shutdown_comp,0,0,0,2}}}

OTP との連携について

OTP との連携はどうするべきなのでしょうか。

TCP っぽく1セッション1gen_server 方式を取ってみる

それぞれのセッションに対して state も保持したいことが多いと思うので、gen_sctp:open するディスパッチャを表にたたせて一つ一つの接続は supervisor:start_child でセッション管理用の gen_server を追加する方式です。

そうすればディスパッチャーはは受け取ったパケットの association_id を見て、ディスパッチするという流れを考えています。もちろん shutdown_comp 等を受け取ったら gen_server:terminate すれば問題ありません。

gen_server をたてるときに gen_server_IP_Port_AssciateId などで動的に atom を生成する必要があるので、セッションを受け取る側を制限する必要があります。

これは仕様なので回避しようがありません。もしくはセッションテーブルを ets で持つとか考えられますが、個人的にはあまり好きではないので ... 。

gen_server と gen_sctp の連携は proc_lib を使えば特に困ることはなさそうです。

  • sctp_dispatcher_sup.erl
  • sctp_dispatcher.erl
  • sctp_association_sup.erl
  • sctp_association.erl

簡単に考えると上記の構成になりそうです。sctp_dispatcher_sup に sctp_dispatcher と sctp_association_sup がぶら下がる形です。そこで comm_up するたびに supervisor:start_child を行うのですが、ここで注意するべき事があります。sctp_association には名前をつける必要があります。one-to-one スタイルであれば完全にクローズ状態になるため問題ないのですが、ディスパッチャがメッセージを配送する必要があるためです。

IP が複数持てますが、そこは後で考えることにします。

名前つける関数例

sctp_name(IP, Port, AssociationId) ->

One-to-Many ではこれがスマートかなと考えていますがあくまでも机上の空論に過ぎません。#3 では OTP + gen_sctp の実装例について解説する予定です。

参考資料

Erlang の gen_sctp は RFC Draft の SCTP API がベースになっている。

基本的な解釈は RFC を読めばわかる。

その他の RFC については wikipedia の RFCs を参考するのがおすすめ