Erlang + ranchでTCPサーバー
Erlang製サーバーcowboyから汎用サーバー部分を分離したranchを使ってTCPサーバー実装する方法のメモ
アプリケーション作成
rebarを落としてranch_sampleて名前のアプリケーションを作る
すでにあるアプリケーションに組み込む場合は不要ですが。
$ mkdir ranch_sample $ cd ranch_sample $ wget https://github.com/rebar/rebar/wiki/rebar $ chmod 755 rebar $ ./rebar create-app appid=ranch_sample
rebar.configを作成してdepsにranchを追加。こんな感じで
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 ft=erlang et {erl_opts, [warnings_as_errors, warn_export_all, warn_unused_import, warn_untyped_record ]}. {xref_checks, [undefined_function_calls]}. {sub_dirs, ["rel"]}. {edoc_opts, [{doclet, edown_doclet}, {dialyzer_specs, all}, {report_missing_type, true}, {report_type_mismatch, true}, {pretty_print, erl_pp}, {preprocess, true}]}. {validate_app_modules, true}. {deps, [ {ranch, "0.8.*", {git, "git://github.com/extend/ranch.git", {branch, "master"}}} ]}.
とりあえずここまでのところで、ranchの取得、コンパイルが正常に行えることを確認。
$ ./rebar get-deps $ ./rebar compile
サーバーの実装
まずはアプリケーション起動時にranch起動するようにする。
src/ranch_sample_app.erl
-module(ranch_sample_app). -behaviour(application). %% Application callbacks -export([start/2, stop/1]). %% =================================================================== %% Application callbacks %% =================================================================== start(_StartType, _StartArgs) -> NbAcceptors = 100, {ok, _} = ranch:start_listener(sample, NbAcceptors, ranch_tcp, [{port, 5555}], sample_protocol, []), rebar_sample_sup:start_link(). stop(_State) -> ok.
start/2の中でranchを起動しています。
NbAcceptorsは、クライアントからのコネクションを待機するプロセスの数。
それとsample_protocolは各コネクションに対する処理を移譲するモジュール名。
こんな感じ。ほぼexampleのecho_serverのままw
-module(sample_protocol). %% API -export([start_link/4, init/4]). %%%=================================================================== %%% API %%%=================================================================== %%-------------------------------------------------------------------- %% @doc %% @end %%-------------------------------------------------------------------- start_link(Ref, Socket, Transport, Opts) -> Pid = spawn_link(?MODULE, init, [Ref, Socket, Transport, Opts]), {ok, Pid}. init(Ref, Socket, Transport, _Opts = []) -> ok = ranch:accept_ack(Ref), loop(Socket, Transport). %%%=================================================================== %%% Internal functions %%%=================================================================== loop(Socket, Transport) -> case Transport:recv(Socket, 0, 5000) of {ok, Data} -> Response = list_to_binary([<<"You say ">>, Data]), Transport:send(Socket, Response), loop(Socket, Transport); _ -> ok = Transport:close(Socket) end.
動作確認
起動してみる
$ erl -pa ebin deps/*/ebin
ranchとrench_sampleを起動
> application:start(ranch). ok > application:start(ranch_sample). ok
この状態でtelnetから接続してみる
$ telnet localhost 5555 hello #<- 入力 you say hello #<- サーバーからのレスポンス
あとはloopのところで実際の処理を書き足していく感じなのかな?