├── ebin └── .gitignore ├── Emakefile ├── test └── test.erl ├── src ├── kl_tserver.erl ├── tserver_sup.erl └── tserver.erl └── README.md /ebin/.gitignore: -------------------------------------------------------------------------------- 1 | *.beam 2 | -------------------------------------------------------------------------------- /Emakefile: -------------------------------------------------------------------------------- 1 | {'src/*', 2 | [debug_info, 3 | {outdir,"ebin"} 4 | ]}. 5 | 6 | -------------------------------------------------------------------------------- /test/test.erl: -------------------------------------------------------------------------------- 1 | -module(test). 2 | -compile(export_all). 3 | -export([handle_data/3, handle_accept/2, handle_close/2]). 4 | -behaviour(kl_tserver). 5 | 6 | start() -> 7 | kl_tserver:start_link(?MODULE, 10000, []). 8 | 9 | handle_data(Sock, Data, State) -> 10 | io:format("recv data~n"), 11 | gen_tcp:send(Sock, Data), 12 | {ok, State}. 13 | 14 | handle_accept(Sock, State) -> 15 | io:format("accept new client~n"), 16 | gen_tcp:send(Sock, "hello"), 17 | {ok, State}. 18 | 19 | handle_close(_Sock, _State) -> 20 | io:format("sock close~n"). 21 | 22 | -------------------------------------------------------------------------------- /src/kl_tserver.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% kl_tserver.erl 3 | %% Kevin Lynx 4 | %% 05.03.2013 5 | %% 6 | -module(kl_tserver). 7 | -export([behaviour_info/1]). 8 | -export([start_link/3, start_link/4, stop/1]). 9 | 10 | behaviour_info(callbacks) -> 11 | [{handle_data, 3}, 12 | {handle_accept, 2}, 13 | {handle_close, 2}]; 14 | behaviour_info(_Other) -> 15 | undefined. 16 | 17 | start_link(Callback, Port, UserArgs) -> 18 | start_link(Callback, undefined, Port, UserArgs). 19 | 20 | start_link(Callback, IP, Port, UserArgs) -> 21 | tserver_sup:start_link(Callback, IP, Port, UserArgs). 22 | 23 | stop(Pid) -> 24 | exit(Pid, normal). 25 | 26 | -------------------------------------------------------------------------------- /src/tserver_sup.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% tserver_sup.erl 3 | %% Kevin Lynx 4 | %% 05.03.2013 5 | %% 6 | -module(tserver_sup). 7 | -behaviour(supervisor). 8 | -export([start_link/4, start_child/1]). 9 | -export([init/1]). 10 | 11 | start_link(Callback, IP, Port, UserArgs) -> 12 | {ok, Pid} = supervisor:start_link(?MODULE, [Callback, IP, Port, UserArgs]), 13 | start_child(Pid), 14 | {ok, Pid}. 15 | 16 | start_child(Server) -> 17 | supervisor:start_child(Server, []). 18 | 19 | init([Callback, IP, Port, UserArgs]) -> 20 | BasicSockOpts = [binary, 21 | {active, false}, 22 | {reuseaddr, true}], 23 | SockOpts = case IP of 24 | undefined -> BasicSockOpts; 25 | _ -> [{ip,IP} | BasicSockOpts] 26 | end, 27 | {ok, LSock} = gen_tcp:listen(Port, SockOpts), 28 | Server = {tserver, {tserver, start_link, 29 | [Callback, LSock, UserArgs]}, 30 | temporary, brutal_kill, worker, [tserver]}, 31 | RestartStrategy = {simple_one_for_one, 1000, 3600}, 32 | {ok, {RestartStrategy, [Server]}}. 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## kl_tserver 2 | 3 | A simple erlang TCP server based on `supervisor` and `gen_server` OTP behaviours. You can use this library to create a TCP server without consideration about connection management. 4 | 5 | ### Usage 6 | 7 | * compile 8 | 9 | In the library root directory: 10 | 11 | erl -make 12 | 13 | This will compile all source in `src` directory and output all `beam` file into `ebin` directory. 14 | 15 | * start `erl` and load the library 16 | 17 | erl -pa ebin 18 | 19 | * create a simple test server, i.e: 20 | 21 | -module(test). 22 | -compile(export_all). 23 | -export([handle_data/3, handle_accept/2, handle_close/2]). 24 | -behaviour(kl_tserver). 25 | 26 | start() -> 27 | kl_tserver:start_link(?MODULE, 10000, []). 28 | 29 | handle_data(Sock, Data, State) -> 30 | io:format("recv data~n"), 31 | gen_tcp:send(Sock, Data), 32 | {ok, State}. 33 | 34 | handle_accept(Sock, State) -> 35 | io:format("accept new client~n"), 36 | gen_tcp:send(Sock, "hello"), 37 | {ok, State}. 38 | 39 | handle_close(_Sock, _State) -> 40 | io:format("sock close~n"). 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/tserver.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% tserver.erl 3 | %% Kevin Lynx 4 | %% 05.03.2013 5 | %% 6 | -module(tserver). 7 | -behaviour(gen_server). 8 | -export([init/1, handle_info/2, handle_cast/2, handle_call/3, code_change/3, terminate/2]). 9 | -export([start_link/3]). 10 | -compile(export_all). 11 | -record(state, {lsock, mod, arg, parent}). 12 | 13 | start_link(M, LSock, Arg) -> 14 | gen_server:start_link(?MODULE, [LSock, M, Arg, self()], []). 15 | 16 | init([LSock, M, Arg, Parent]) -> 17 | {ok, #state{lsock = LSock, mod = M, arg = Arg, parent = Parent}, 0}. 18 | 19 | handle_info({tcp, Socket, RawData}, #state{mod = M, arg = Arg} = State) -> 20 | case M:handle_data(Socket, RawData, Arg) of 21 | {close, NewArg} -> 22 | gen_tcp:close(Socket), 23 | {noreply, State#state{arg = NewArg}}; 24 | {ok, NewArg} -> 25 | inet:setopts(Socket, [{active,once}]), 26 | {noreply, State#state{arg = NewArg}} 27 | end; 28 | 29 | handle_info({tcp_closed, Socket}, #state{mod = M, arg = Arg} = State) -> 30 | M:handle_close(Socket, Arg), 31 | {stop, normal, State}; 32 | 33 | handle_info(timeout, #state{lsock = LSock, mod = M, arg = Arg, parent = Parent} = State) -> 34 | {ok, Sock} = gen_tcp:accept(LSock), 35 | case M:handle_accept(Sock, Arg) of 36 | {close, NewArg} -> 37 | gen_tcp:close(Sock), 38 | {noreply, State#state{arg = NewArg}}; 39 | {ok, NewArg} -> 40 | tserver_sup:start_child(Parent), 41 | inet:setopts(Sock, [{active,once}]), 42 | {noreply, State#state{lsock = Sock, arg = NewArg}} 43 | end. 44 | 45 | handle_call(Msg, _From, State) -> 46 | {reply, {ok, Msg}, State}. 47 | 48 | handle_cast(stop, State) -> 49 | {stop, normal, State}. 50 | 51 | terminate(_Reason, _State) -> 52 | ok. 53 | 54 | code_change(_OldVsn, State, _Extra) -> 55 | {ok, State}. 56 | 57 | --------------------------------------------------------------------------------