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

gen_sctp 試してみた #1

erlang sctp

いいわけ

資料を読みながら SCTP とは何だろうということから入ってみた。でも結局は素人の知識でしかないので、役に立たないと思いますが、自分向けのメモってことで。間違ってる可能性大。

自分の理解のために説明していますがごちゃごちゃしてますね。なんとかもう一度きれいにまとめ直します。とりあえずやってみた程度で。

とりあえず

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

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

まずサーバとクライアントですが、コード上からはサーバは特に何もしてないことがわかります。クライアントがストリームの数も決めてますし、どのストリームに送るかどうかもクライアントです。まぁストリームの選択はクライアントとサーバどっちでもいいんでしょうけど。

とりあえず gen_sctp:recv する際に返ってくる値をパターンマッチしてみました。record で返ってくるのは record でキャッチしてます。#sctp_paddr_change{} で中身があろうとなかろうとキャッチ出来たりします。

以下のソースからわかるのは ... MsgBin を使ってDATA チャンクが送られて来ており、 #sctp_sndrcvinfo{} が AncData に入ってきます。

データではなくセッションが切れたという場合は AncData がからで MsgBin の中に #sctp_assoc_change{} が入ってきます。

切れたときは #sctp_assoc_change{state} に comm_lost や shutdown_comp が入ります。comm_lost は gen_sctp:abort で強制的にセッションを切るときに起きるようです。abort は今バッファにたまってるデータを破棄してサーバ側にセッションの切断を強制します。shutdown_comp は gen_sctp:close を使って正しく終了した場合に送られます。#sctp_shutdown_event{} が送られてからその後 shutdown_comp で、#sctp_assoc_change{} が送られます。

comm_lost の場合は shutdown_comp が comm_lost になっています。さらにサーバだけで完結していたのがクライアントにも comm_lost であることを送って来ています。

  • comm_up はデータを受け取る準備が出来た状態
  • comm_lost はアソシエーションが失敗してクローズドに移行した状態
  • shutdown_comp は問題なくクローズドにアソシエーションが移行した状態

sctp_paddr_change の addr_confirmed(2) はこのアドレスを承認したことをサーバとクライアント同士でやりとりします。

ここのソースの流れとしてはまず、ネゴして #sctp_assoc_change{state=comm_up} をサーバにクライアントが送って、サーバはクライアントに #sctp_assoc_change{state=comm_up} を送ります。
つぎにクライアントが初めてパケット送ったとき #sctp_sndrcvinfo{} が送られます。そしてその後サーバは #sctp_paddr_change{state=add_confirmed} を送ります。これは any で起動しているため、サーバが IP を今回の接続に割り当てています。

そしてクライアントから別のストリームでデータを送っています。最初は 0 番でしたが、次は 5 番で送っています。ストリームは 0 から 9 まであります。これは最初に connect する際に #sctp_initmsg{num_ostreams=10} で宣言しています。

そしてクライアントは gen_sctp:abort で接続を強制的に切ろうとしているため、強制的にサーバは切断されます。そのときに comm_lost が送られます。

もし gen_sctp:close で正常に終了する場合は shutdown_event が送られます。

shutdown_comp

server

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

client

Connection Successful, Assoc={sctp_assoc_change,comm_up,0,10,10,9}
ok
Received: {ok,{{127,0,0,1},
               2009,[],
               {sctp_paddr_change,{{192,168,0,24},2009},addr_confirmed,2,9}}}
ok
ok
comm_lost

server

Received: {ok,{{127,0,0,1},52150,[],{sctp_assoc_change,comm_up,0,10,10,12}}}
Received: {ok,{{127,0,0,1},
               52150,
               [{sctp_sndrcvinfo,0,0,[],0,0,0,212043851,0,12}],
               <<"Test 0">>}}
Received: {ok,{{127,0,0,1},
               52150,
               [{sctp_sndrcvinfo,5,0,[],0,0,0,212043852,0,12}],
               <<"Test 5">>}}
Received: {ok,{{127,0,0,1},
               52150,[],
               {sctp_paddr_change,{{192,168,0,24},52150},
                                  addr_confirmed,2,12}}}
Received: {ok,{{127,0,0,1},
               52150,[],
               {sctp_assoc_change,comm_lost,3072,0,0,12}}}

client

Connection Successful, Assoc={sctp_assoc_change,comm_up,0,10,10,11}
ok
ok
Received: {ok,{{127,0,0,1},
               2009,[],
               {sctp_paddr_change,{{192,168,0,24},2009},addr_confirmed,2,11}}}
ok
Received: {ok,{{127,0,0,1},2009,[],{sctp_assoc_change,comm_lost,3072,0,0,11}}}
ok

ソース

sctp2_server

-module(sctp2_server).

-compile(export_all).

-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/include/inet_sctp.hrl").

start_link() ->
  proc_lib:start_link(?MODULE, init, [self(), any, 2009]).

start_link(Host, Port) when is_list(Host), is_list(Port) ->
  {ok, #hostent{h_addr_list = [IP|_]}} = inet:gethostbyname(Host),
  io:format("~w -> ~w~n", [Host, IP]),
  proc_lib:start_link(?MODULE, init, [self(), IP, Port]).

init(Pid, IP, Port) ->
  proc_lib:init_ack(Pid, {ok, self()}),
  {ok, Socket} = gen_sctp:open([{ip,IP},{port,Port},{recbuf,65536}]),
  io:format("Listening on ~w:~w. ~w~n", [IP, Port, Socket]),
  ok = gen_sctp:listen(Socket, true),
  loop(Socket).

loop(Socket) ->
  case gen_sctp:recv(Socket) of
    {error, Error} ->
      io:format("SCTP RECV ERROR: ~p~n", [Error]);
    % #sctp_paddr_change{
    %       addr      = {ip_address(),port()},
    %       state     = atom(),
    %       error     = int(),
    %       assoc_id  = assoc_id()
    % }      
    {ok, {_FromIP, _FromPort, [], #sctp_paddr_change{}}} = Data ->
      io:format("Received: ~p~n", [Data]);
    % #sctp_assoc_change{
    %       state             = atom(),
    %       error             = atom(),
    %       outbound_streams  = int(),
    %       inbound_streams   = int(),
    %       assoc_id          = assoc_id()
    % }        
    {ok, {_FromIP, _FromPort, [], #sctp_assoc_change{
				    state=comm_lost}
				  }} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, [], #sctp_assoc_change{
				    state=shutdown_comp}
				  }} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, [], _BinMsg}} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, _AncData, _BinMsg}} = Data ->
      io:format("Received: ~p~n", [Data])
  end,
  loop(Socket). 

sctp2_client

-module(sctp2_client).

-compile(export_all).

-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/include/inet_sctp.hrl").

client() ->
  client([localhost]).

client([Host]) ->
  client(Host, 2009);

client([Host, Port]) when is_list(Host), is_list(Port) ->
  client(Host,list_to_integer(Port)),
  init:stop().

client(Host, Port) when is_integer(Port) ->
  {ok, Socket} = gen_sctp:open(),
  {ok, Assoc} = gen_sctp:connect
      (Socket, Host, Port, [{sctp_initmsg,#sctp_initmsg{num_ostreams=10}}]),
  _Pid = spawn(?MODULE, loop, [Socket]),
  io:format("Connection Successful, Assoc=~p~n", [Assoc]),
  io:write(gen_sctp:send(Socket, Assoc, 0, <<"Test 0">>)),
  io:nl(),
  timer:sleep(3000),
  io:write(gen_sctp:send(Socket, Assoc, 5, <<"Test 5">>)),
  io:nl(),
  timer:sleep(3000),
  %io:write(gen_sctp:abort(Socket, Assoc)),
  %io:nl(),
  timer:sleep(500),
  gen_sctp:close(Socket). 

loop(Socket) ->
  case gen_sctp:recv(Socket) of
    {error, Error} ->
      io:format("SCTP RECV ERROR: ~p~n", [Error]);
    % #sctp_paddr_change{
    %       addr      = {ip_address(),port()},
    %       state     = atom(),
    %       error     = int(),
    %       assoc_id  = assoc_id()
    % }      
    {ok, {_FromIP, _FromPort, [], #sctp_paddr_change{}}} = Data ->
      io:format("Received: ~p~n", [Data]);
    % #sctp_assoc_change{
    %       state             = atom(),
    %       error             = atom(),
    %       outbound_streams  = int(),
    %       inbound_streams   = int(),
    %       assoc_id          = assoc_id()
    % }        
    {ok, {_FromIP, _FromPort, [], #sctp_assoc_change{
				    state=comm_up}
				  }} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, [], #sctp_assoc_change{
				    state=comm_lost}
				  }} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, [], #sctp_assoc_change{
				    state=shutdown_comp}
				  }} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, [], _BinMsg}} = Data ->
      io:format("Received: ~p~n", [Data]);
    {ok, {_FromIP, _FromPort, _AncData, _BinMsg}} = Data ->
      io:format("Received: ~p~n", [Data])
  end,
  loop(Socket).