这篇主要就是展示lib_chan的详细代码。
一:lib_chan
%% socket_dist/lib_chan.erl
-module(lib_chan).
-export([cast/2, start_server/0, start_server/1, connect/5, disconnect/1, rpc/2]).
-import(lists, [map/2, member/2, foreach/2]).
-import(lib_chan_mm, [send/2,close/1]).
%%-----------------------------------------------------------------------------------
%% 服务器代码
start_server() ->
case os:getenv("HOME") of
false ->
exit({ebadEnv, "HOME"});
Home ->
start_server(Home + "/erlang_config/lib_chan.conf")
end.
start_server(ConfigFile) ->
io:format("lib_chan starting:~p~n", [ConfigFile]),
case file:consult(ConfigFile)of
{ok, ConfigData} ->
io:format("ConfigData=~p~n",[ConfigData]),
case check_terms(ConfigData)of
[] ->
start_serverl(ConfigData);
Errors ->
exit({eDaemonConfig, Errors})
end;
{error, why} ->
exit({eDaemonConfig, Why})
end.
%% check_terms() -> [Error]
check_terms(ConfigData) ->
L = map(fun check_term/1, ConfigData),
[X || {error, X} <- L].
check_term({port, P}) when is_integer(P) -> ok;
check_term({service, _, password, _, mfa, _, _, _}) -> ok;
check_term(X) -> {error,{badTerm,X}}.
start_serverl(ConfigData) ->
register(lib_chan, spawn(fun() -> start_server2(ConfigData) end)).
start_server2(ConfigData) ->
[Port] = [P || {port,P} <- ConfigData],
start_port_server(Port, ConfigData).
start_port_server(Port, ConfigData) ->
lib_chan_cs:start_raw_server(Port,
fun(Socket) ->
start_port_instance(Socket,ConfigData)
end, 100, 4).
start_port_instance(Socket, ConfigData) ->
%% 这是处理底层连接的位置
%% 但首先要分裂出一个连接处理进程,必须成为中间人
S = self(),
Controller = spawn_link(fun() -> start_erl_port_server(S, ConfigData) end),
lib_chan_mm:1oop(Socket, Controller).
start_erl_port_server(MM, ConfigData) ->
receive
{chan, MM, {startservice, Mod, ArgC}} ->
case get_service_definition(Mod, ConfigData) of
{yes, Pwd, MFA} ->
case Pwd of
none ->
send(MM, ack),
really_start(MM, ArgC, MFA);
_ ->
do_authentication(Pwd, MM, ArgC, MFA)
end;
no ->
io:format("sending bad service~n"),
send(MM, badService),
close(MM)
end;
Any ->
io:format("***ErL port server got:~p ~p~n", [MM, Any]),
exit({protocolViolation, Any})
end.
do_authentication(Pwd, MM, ArgC, MFA) ->
C = lib_chan_auth:make_challenge(),
send(MM, {challenge, C}),
receive
{chan, MM, {response, R}} ->
case lib_chan_auth:is_response_correct(C, R, Pwd) of
true ->
send(MM, ack),
really_start(MM, ArgC, MFA);
false ->
send(MM, authFail),
close(MM)
end
end.
%% MM是中间人
%% Mod是我们想要执行的模块。ArgC和ArgS分别来自客户端和服务器
really_start(MM, ArgC, {Mod, Func, ArgS}) ->
%% 认证成功,现在开始工作
case (catch appty(Mod, Func, [MM, ArgC, Args])) of
{'EXIT', normal} ->
true;
{'EXIT', why} ->
io:format("server error:~p-n", [Why]);
why ->
io:format("server error should die with exit(normal)was:~p-n", [Why])
end.
%% get_service_definition(Name, ConfigData)
get_service_definition(Mod, [{service, Mod, password, Pwd, mfa, M, F, A}]) ->
{yes, Pwd, {M, F, A}};
get_service_definition(Name, [_ | T]) ->
get_service_definition(Name, T);
get_service_definition(_, []) ->
no.
%%----------------------------------------------------------------------------------
%% 客户端连接代码
%% connect(...) -> {ok, MM} | Error
connect(Host, Port, Service, Secret, ArgC) ->
s = self(),
MM = spawn(fun() -> connect(S, Host, Port) end),
receive
{M, ok} ->
case authenticate(MM, Service, Secret, ArgC) of
ok -> {ok, MM};
Error -> Error
end;
{MM, Error} ->
Error
end.
connect(Parent, Host, Port) ->
case lib_chan_cs:start_raw_client(Host, Port, 4) of
{ok, Socket} ->
Parent ! {self(), ok},
lib_chan_mm:loop(Socket, Parent);
Error ->
Parent ! {self(),Error}
end.
authenticate(MM, Service, Secret, ArgC) ->
send(MM, {startservice, Service, ArgC}),
%% 应该会收到质询、ack或者套接字已关闭的消息
receive
{chan, MM, ack} ->
ok;
{chan, MM, {challenge, C}} ->
R = lib_chan_auth:make_response(C, Secret),
send(MM, {response, R}),
receive
{chan, MM, ack} ->
ok:
{chan, MM, authFail} ->
wait_close(MM),
{error, authFail};
Other ->
{error, Other}
end;
{chan, MM, badService} ->
wait_close(MM),
{error, badservice};
other ->
{error, Other}
end.
wait_closc(MM) ->
receive
{chan_closed, MM} ->
true
after 5000 ->
io:format("**error lib_chan~n"),
true
end.
disconnect(MM) -> close(MM).
rpc(MM, Q) ->
send(MM, Q),
receive
{chan, MM, Reply} ->
Reply
end.
cast(MM, Q) ->
send(MM, Q).
二:lib_chan_cs
%% socket_dist/lib_chan_cs.erl
-module(lib_chan_cs).
%% cs代表client server
-export([start_raw_server/4, start_raw_client/3]).
-export([stop/1]).
-export([children/1]).
%% start_raw_server(Port, Fun, Max, PacketLength)
%% 这个服务器在Port上接受最多MaX个连接
%% 首次连接Port时,会调用Fun(Socket)
%% 此后发给套接字的消息会转换成发给处理进程的消息
%% PacketLength:通常是0、1、2或4.
%% tcp_server的典型用法如下[l]:
%% 设立一个监听器
%%
%% start_agent(Port) ->
%% process_flag(trap_exit, true),
%% lib_chan_server:start_raw_server(Port,
%% fun(Socket) -> input_handler(Socket) end,
%% 15,0).
start_raw_client(Host, Port, PacketLength) ->
gen_tcp:connect(Host, Port,
[binary, {active, true}, {packet, PacketLength}]).
%% 注意:当start_raw_server.返回时
%% 它应该已经准备好立即接受请求了
start_raw_server(Port, Fun, Max, PacketLength) ->
Name = port_name(Port),
case whereis(Name) of
undefined ->
Self = self(),
Pid = spawn_link(fun() ->
cold_start(Self, Port, Fun, Max, PacketLength)
end),
receive
{Pid, ok} ->
register(Name, Pid),
{ok, self()};
{Pid, Error} ->
Error
end;
_Pid ->
{error, already_started}
end.
stop(Port) when integer(Port) ->
Name = port_name(Port),
case whereis(Name) of
undefined ->
not_started;
Pid ->
exit(Pid, kill),
(catch unregister(Name)),
stopped
end.
children(Port) when integer(Port) ->
port_name(Port) ! {children, self()},
receive
{session_server, Reply} -> Reply
end.
port_name(Port) when integer(Port) ->
list_to_atom("portserver" + integer_to_list(Port)).
cold_start(Master, Port, Fun, Max, PacketLength) ->
process_flag(trap_exit, true),
%% 现在我们准备好运行了
case gen_tcp:listen(Port, [binary,
%% {dontroute, true},
{nodelay, true},
{packet, PacketLength},
{reuseaddr, true},
{active, true}]) of
{ok, Listen} ->
%%io:format("Listening to:~p~n", [Listen]),
Master ! {self(), ok},
New = start_accept(Listen, Fun),
%% Now we're ready to run
socket_loop(Listen, New, [], Fun, Max);
Error ->
Master ! {self(), Error}
end.
socket_loop(Listen, New, Active, Fun, Max) ->
receive
{istarted, New} ->
Activel = [New | Active],
possibly_start_another(false, Listen, Activel, Fun, Max);
{'EXIT', New,Why} ->
%%io:format("Child exit=~p~n",[Why]),
possibly_start_another(false, Listen, Active, Fun, Max);
{'EXIT', Pid, Why} ->
%%io:format("Child exit=~p~n", [Why]),
Activel = lists:delete(Pid, Active),
possibly_start_another(New, Listen, Activel, Fun, Max);
{children, From} ->
From ! {session_server, Active},
socket_loop(Listen, New, Active, Fun, Max);
_other ->
socket_loop(Listen, New, Active, Fun, Max)
end.
possibly_start_another(New, Listen, Active, Fun, Max) when pid(New) ->
socket_loop(Listen, New, Active, Fun, Max);
possibly_start_another(false, Listen, Active, Fun, Max) ->
case length(Active) of
N when N < Max ->
New = start_accept(Listen, Fun),
socket_loop(Listen, New, Active, Fun, Max);
_ ->
socket_loop(Listen, false, Active, Fun, Max)
end.
start_accept(Listen, Fun) ->
S = self(),
spawn_link(fun() -> start_child(S, Listen, Fun) end).
start_child(Parent, Listen, Fun) ->
case gen_tcp:accept(Listen) of
{ok, Socket} ->
Parent ! {istarted, self()}, %% 告知控制器
inet:setopts(Socket, [{packet, 4},
binary,
{nodelay,true},
{active,true}]),
%% 激活套接字之前
%% io:format("running the child:~p Fun=~p~n", [Socket, Fun]),
process_flag(trap_exit, true),
case (catch Fun(Socket)) of
{'EXIT', normal} ->
true;
{'EXIT', Why} ->
io:format("Port process dies with exit:~p~n", [why]),
true;
_ ->
%% 不是退出消息,说明一切顺利
true
end
end.
end.
三:lib_chan_mm
%% socket_dist/lib_chan_mm.erl
%% 协议
%% 发往控制进程
%% {chan, MM, Term}
%% {chan_closed, MM}
%% 来自任意进程
%% {send, Term}
%% close
-module(lib_chan_mm).
%% TCP中间人
%% 模拟gen_tcp接口
-export([loop/2, send/2, close/1, controller/2, set_trace/2, trace_with_tag/2]).
send(Pid, Term) -> Pid ! {send, Term}.
close(Pid) -> Pid ! close.
controller(Pid, Pidl) -> Pid ! {setController, Pidl}.
set_trace(Pid, X) -> Pid ! {trace, X}.
trace_with_tag(Pid, Tag) ->
set_trace(Pid, {true,
fun(Msg) ->
io:format("MM:-p -p-n",[Tag,Msg])
end}).
loop(Socket, Pid) ->
%% trace_with_tag(self(), trace)
process_flag(trap_exit, true),
loop1(Socket, Pid, false).
loop1(Socket, Pid, Trace) ->
receive
{tcp, Socket, Bin} ->
Term = binary_to_term(Bin),
trace_it(Trace, {socketReceived, Term}),
Pid ! {chan,self(), Term},
loop1(Socket, Pid, Trace);
{tcp_closed, Socket} ->
trace_it(Trace, socketclosed),
Pid ! {chan_closed, self()};
{'EXIT', Pid, Why} ->
trace_it(Trace, {controllingProcessExit, Why}),
gen_tcp:close(Socket);
{setController, Pidl} ->
trace_it(Trace, {changedController, Pid}),
loop1(Socket,Pidl,Trace);
{trace, Tracel} ->
trace_it(Trace, {setTrace, Trace1}),
loop1(Socket, Pid, Trace1);
close ->
trace_it(Trace, closedByclient),
gen_tcp:close(Socket);
{send, Term} ->
trace_it(Trace, {sendingMessage, Term}),
gen_tcp:send(Socket, term_to_binary(Term)),
loop1(Socket, Pid, Trace);
UUg ->
io:format("lib_chan_mm:protocol error:~p~n",[UUg]),
loop1(Socket, Pid, Trace)
end.
trace_it(false, _) -> void;
trace_it({true, F}, M) -> F(M).
四:lib_chan_auth
%% socket_dist/lib_chan_auth.erl
-module(lib_chan_auth).
-export([make_challenge/0, make_response/2, is_response_correct/3]).
make_challenge() ->
random_string(25).
make_response(Challenge, Secret) ->
lib_md5:string(Challenge ++ Secret).
is_response_correct(Challenge, Response, Secret) ->
case lib_md5:string(Challenge ++ Secret) of
Response -> true;
_ -> false
end.
%% random_string(N) -> 一个随机字符串(内含N个字符)
random_string(N) -> random_seed(), random_string(N,[]).
random_string(0, D) -> D;
random_string(N, D) ->
random_string(N-1, [random:uniform(26)-1+$a|D]).
random_seed() ->
{_, _, x} = erlang:now(),
{H, M, S} = time(),
H1 = H * X rem 32767,
M1 = M * X rem 32767,
S1 = S * X rem 32767,
put(random_seed, {H1, M1, S1}).
具体基础知识和详细介绍可以点击下面链接跳转到另一篇博客中: