lib_chan代码

这篇主要就是展示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}).

具体基础知识和详细介绍可以点击下面链接跳转到另一篇博客中:

http://t.csdnimg.cn/OAaBz

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明明如皓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值