IRC程序学习

 

%%聊天的中转站,将{chan,MM,Msg}形式的信息转化为 {mm, MM, Msg}形式

-module(mod_chat_controller).

-export([start/3]).

-import(lib_chan_mm, [send/2]).



start(MM, _, _) ->

    process_flag(trap_exit, true),

    io:format("mod_chat_controller off we go ...~p~n",[MM]),

    loop(MM).



loop(MM) ->

     receive

     {chan, MM, Msg} ->

         chat_server ! {mm, MM, Msg},

         loop(MM);

     {'EXIT', MM, _Why} ->

         chat_server ! {mm_closed, MM};

     Other ->

         io:format("mod_chat_controller unexpected message =~p (MM=~p)~n",

               [Other, MM]),

         loop(MM)

    end.
mod_chat_controller

 

%%聊天服务器

-module(chat_server).

-import(lib_chan_mm, [send/2, controller/2]).

-import(lists, [delete/2, foreach/2, map/2, member/2,reverse/2]).



-compile(export_all).



%%用chat.conf的配置启动服务器,每个连接到服务器的socket将调用mod_chat_controller模块的start函数

%%{port, 2223}.

%%{service, chat, password,"AsDT67aQ",mfa,mod_chat_controller,start,[]}.

start() ->

    start_server(),

    lib_chan:start_server("D:\\code\\erlcode\\socket_dist\\chat.conf").



start_server() ->

    register(chat_server, 

         spawn(fun() ->

               process_flag(trap_exit, true),

               Val= (catch server_loop([])),

               io:format("Server terminated with:~p~n",[Val])

           end)).





%%新启的进程,注册为chat_server,用来接收客户端用户的登录消息,当收到某一群组的登录信息时,如果该群组已经存在则传消息给该群组,如果不在则创建群组

server_loop(L) ->

    receive

    {mm, Channel, {login, Group, Nick}} ->

        case lookup(Group, L) of

        {ok, Pid} ->

            Pid ! {login, Channel, Nick},

            server_loop(L);

        error ->

            Pid = spawn_link(fun() ->

                         chat_group:start(Channel, Nick) 

                     end),

            server_loop([{Group,Pid}|L])

        end;

    {mm_closed, _} ->

        server_loop(L); 

    {'EXIT', Pid, allGone} ->

        L1 = remove_group(Pid, L),

        server_loop(L1);

    Msg ->

        io:format("Server received Msg=~p~n",

              [Msg]),

        server_loop(L)

    end.







lookup(G, [{G,Pid}|_]) -> {ok, Pid};

lookup(G, [_|T])       -> lookup(G, T);

lookup(_,[])           -> error.



remove_group(Pid, [{G,Pid}|T]) -> io:format("~p removed~n",[G]), T;

remove_group(Pid, [H|T])       -> [H|remove_group(Pid, T)];

remove_group(_, [])            -> [].
chat_server

 

%%聊天群组,包含各个连接进该群组的进程id

-module(chat_group).

-import(lib_chan_mm, [send/2, controller/2]).

-import(lists, [foreach/2, reverse/2]).



-export([start/2]).



start(C, Nick) ->

    process_flag(trap_exit, true),

    controller(C, self()),

    send(C, ack),

    self() ! {chan, C, {relay, Nick, "I'm starting the group"}},

    group_controller([{C,Nick}]).







delete(Pid, [{Pid,Nick}|T], L) -> {Nick, reverse(T, L)};

delete(Pid, [H|T], L)          -> delete(Pid, T, [H|L]);

delete(_, [], L)               -> {"????", L}.







group_controller([]) ->

    exit(allGone);

group_controller(L) ->

    receive

    {chan, C, {relay, Nick, Str}} ->

        foreach(fun({Pid,_}) -> send(Pid, {msg,Nick,C,Str}) end, L),

        group_controller(L);

    {login, C, Nick} ->

        controller(C, self()),

        send(C, ack),

        self() ! {chan, C, {relay, Nick, "I'm joining the group"}},

        group_controller([{C,Nick}|L]);

    {chan_closed, C} ->

        {Nick, L1} = delete(C, L, []),

        self() ! {chan, C, {relay, Nick, "I'm leaving the group"}},

        group_controller(L1);

    Any ->

        io:format("group controller received Msg=~p~n", [Any]),

        group_controller(L)

    end.
chat_group

 

-module(chat_client).



-import(io_widget, 

    [get_state/1, insert_str/2, set_prompt/2, set_state/2, 

     set_title/2, set_handler/2, update_state/3]).



-export([start/0, test/0, connect/5]).





start() -> 

    connect("localhost", 2223, "AsDT67aQ", "general", "joe").





test() ->

    connect("localhost", 2223, "AsDT67aQ", "general", "joe"),

    connect("localhost", 2223, "AsDT67aQ", "general", "jane"),

    connect("localhost", 2223, "AsDT67aQ", "general", "jim"),

    connect("localhost", 2223, "AsDT67aQ", "general", "sue").

       



connect(Host, Port, HostPsw, Group, Nick) ->

    spawn(fun() -> handler(Host, Port, HostPsw, Group, Nick) end).

                 

handler(Host, Port, HostPsw, Group, Nick) ->

    process_flag(trap_exit, true),

    Widget = io_widget:start(self()),

    set_title(Widget, Nick),

    set_state(Widget, Nick),

    set_prompt(Widget, [Nick, " > "]),

    set_handler(Widget, fun parse_command/1),

    start_connector(Host, Port, HostPsw),    

    disconnected(Widget, Group, Nick).





%%接收服务器的连接反馈信息,连接上后进行登录

disconnected(Widget, Group, Nick) ->

    receive

    {connected, MM} ->

        insert_str(Widget, "connected to server\nsending data\n"),

        lib_chan_mm:send(MM, {login, Group, Nick}),

        wait_login_response(Widget, MM);

    {Widget, destroyed} ->

        exit(died);

    {status, S} ->

        insert_str(Widget, to_str(S)),

        disconnected(Widget, Group, Nick);

    Other ->

        io:format("chat_client disconnected unexpected:~p~n",[Other]),

        disconnected(Widget, Group, Nick)

    end.





%%接收服务器的登录反馈信息

wait_login_response(Widget, MM) ->

    receive

    {chan, MM, ack} ->

        active(Widget, MM);

    Other ->

        io:format("chat_client login unexpected:~p~n",[Other]),

        wait_login_response(Widget, MM)

    end. 





%%登录后获取客户端的反馈,并接受传到客户端的信息

active(Widget, MM) ->

     receive

     {Widget, Nick, Str} ->

         lib_chan_mm:send(MM, {relay, Nick, Str}),

         active(Widget, MM);

     {chan, MM, {msg, From, Pid, Str}} ->

         insert_str(Widget, [From,"@",pid_to_list(Pid)," ", Str, "\n"]),

         active(Widget, MM);

     {'EXIT',Widget,windowDestroyed} ->

         lib_chan_mm:close(MM);

     {close, MM} ->

         exit(serverDied);

     Other ->

         io:format("chat_client active unexpected:~p~n",[Other]),

         active(Widget, MM)

     end. 





%%链接chat服务器

start_connector(Host, Port, Pwd) ->

    S = self(),

    spawn_link(fun() -> try_to_connect(S, Host, Port, Pwd) end).

    

try_to_connect(Parent, Host, Port, Pwd) ->

    %% Parent is the Pid of the process that spawned this process

    case lib_chan:connect(Host, Port, chat, Pwd, []) of

    {error, _Why} ->

        Parent ! {status, {cannot, connect, Host, Port}},

        sleep(2000),

        try_to_connect(Parent, Host, Port, Pwd);

    {ok, MM} ->

        lib_chan_mm:controller(MM, Parent),

        Parent ! {connected, MM},

        exit(connectorFinished)

    end.





sleep(T) ->

    receive

    after T -> true

    end.

        

to_str(Term) ->

    io_lib:format("~p~n",[Term]).



parse_command(Str) -> skip_to_gt(Str).



skip_to_gt(">" ++ T) -> T;

skip_to_gt([_|T])    -> skip_to_gt(T);

skip_to_gt([])       -> exit("no >").
chat_client

 

-module(io_widget).



-export([get_state/1,

     start/1, test/0, 

     set_handler/2, 

     set_prompt/2,

     set_state/2,

     set_title/2, insert_str/2, update_state/3]).



start(Pid) ->

    gs:start(),

    spawn_link(fun() -> widget(Pid) end).



get_state(Pid)          -> rpc(Pid, get_state).

set_title(Pid, Str)     -> Pid ! {title, Str}.

set_handler(Pid, Fun)   -> Pid ! {handler, Fun}.

set_prompt(Pid, Str)    -> Pid ! {prompt, Str}.

set_state(Pid, State)   -> Pid ! {state, State}.

insert_str(Pid, Str)    -> Pid ! {insert, Str}.

update_state(Pid, N, X) -> Pid ! {updateState, N, X}. 



rpc(Pid, Q) ->    

    Pid ! {self(), Q},

    receive

    {Pid, R} ->

        R

    end.



widget(Pid) ->

    Size = [{width,500},{height,200}],

    Win = gs:window(gs:start(),

            [{map,true},{configure,true},{title,"window"}|Size]),

    gs:frame(packer, Win,[{packer_x, [{stretch,1,500}]},

              {packer_y, [{stretch,10,100,120},

                      {stretch,1,15,15}]}]),

    gs:create(editor,editor,packer, [{pack_x,1},{pack_y,1},{vscroll,right}]),

    gs:create(entry, entry, packer, [{pack_x,1},{pack_y,2},{keypress,true}]),

    gs:config(packer, Size),

    Prompt = " > ",

    State = nil,

    gs:config(entry, {insert,{0,Prompt}}),

    loop(Win, Pid, Prompt, State, fun parse/1). 



loop(Win, Pid, Prompt, State, Parse) ->   

    receive

    {From, get_state} ->

        From ! {self(), State},

        loop(Win, Pid, Prompt, State, Parse);

    {handler, Fun} ->

        loop(Win, Pid, Prompt, State, Fun);

    {prompt, Str} ->

        %% this clobbers the line being input ...

        %% this could be fixed - hint

        gs:config(entry, {delete,{0,last}}),

        gs:config(entry, {insert,{0,Str}}),

        loop(Win, Pid, Str, State, Parse);

    {state, S} ->

        loop(Win, Pid, Prompt, S, Parse);

    {title, Str} ->

        gs:config(Win, [{title, Str}]),

        loop(Win, Pid, Prompt, State, Parse);

    {insert, Str} ->

        gs:config(editor, {insert,{'end',Str}}),

        scroll_to_show_last_line(),

        loop(Win, Pid, Prompt, State, Parse);

    {updateState, N, X} ->

        io:format("setelemtn N=~p X=~p State=~p~n",[N,X,State]),

        State1 = setelement(N, State, X),

        loop(Win, Pid, Prompt, State1, Parse);

    {gs,_,destroy,_,_} ->

        io:format("Destroyed~n",[]),

        exit(windowDestroyed);

    {gs, entry,keypress,_,['Return'|_]} ->

        Text = gs:read(entry, text),

        io:format("io_widget:input text:~p~n",[Text]),

          io:format("io_widget:send text to pid:~p~n",[Pid]),

        gs:config(entry, {delete,{0,last}}),

        gs:config(entry, {insert,{0,Prompt}}),

        try Parse(Text) of

        Term ->

            Pid ! {self(), State, Term}

        catch

        _:_ ->

            self() ! {insert, "** bad input**\n** /h for help\n"}

        end,

        loop(Win, Pid, Prompt, State, Parse);

    {gs,_,configure,[],[W,H,_,_]} ->

        gs:config(packer, [{width,W},{height,H}]),

        loop(Win, Pid, Prompt, State, Parse);

    {gs, entry,keypress,_,_} ->

        loop(Win, Pid, Prompt, State, Parse);

    Any ->

        io:format("Discarded:~p~n",[Any]),

        loop(Win, Pid, Prompt, State, Parse)

    end.



scroll_to_show_last_line() ->

    Size       = gs:read(editor, size),

    Height     = gs:read(editor, height),

    CharHeight = gs:read(editor, char_height),

    TopRow     = Size - Height/CharHeight,

    if  TopRow > 0 -> gs:config(editor, {vscrollpos, TopRow});

    true       -> gs:config(editor, {vscrollpos, 0})

    end.



test() ->

    spawn(fun() -> test1() end).



test1() ->

    W = io_widget:start(self()),

    io_widget:set_title(W, "Test window"),

    loop(W).



loop(W) ->

    receive

    {W, {str, Str}} ->

        Str1 = Str ++ "\n",

        io_widget:insert_str(W, Str1),

        loop(W)

    end.



parse(Str) ->

    {str, Str}.
io_widget

 

你可能感兴趣的:(学习)