%%聊天的中转站,将{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

最新文章

  1. Centos7-mqtt消息中间件mosquitto的安装和配置
  2. poj_2421_mst
  3. JDBC 连接数据库
  4. XTU OJ 1210 Happy Number (暴力+打表)
  5. Intellj Idea 2016.1.3的使用
  6. JavaScript中forEach的用法相关
  7. JAVA GUI学习 - JInternalFrame浮动窗口:可拖拽窗口(依赖于父窗口)
  8. NSLineBreakMode
  9. iOS不可变字符串的所有操作
  10. 吴裕雄 python 机器学习——分类决策树模型
  11. Android 程序崩溃之后fragment出现画面重叠问题
  12. PHP关联查询
  13. C# 子线程调用主线程窗体的解决方法
  14. 【转】用instruments来检验你的app
  15. TOSCA自动化测试工具ppt(正在整理)
  16. BZOJ5280: [Usaco2018 Open]Milking Order(二分+拓扑)
  17. List排序共通代码
  18. NYOJ 42 一笔画
  19. 服务器出现大量close_wait,我们来说说到底是怎么回事?(以tomcat为例)
  20. 前端跨域调请求 nginx反向代理

热门文章

  1. Java线程(一)
  2. 2017-2018-1 20179205《Linux内核原理与设计》第六周作业
  3. Javascript正则表达式详细讲解和示例,通俗易懂
  4. STL不同容器的使用方法
  5. 【VI Script】你不知道的脚本编程
  6. Mac下使用brew搭建PHP7+nginx+mysql开发环境
  7. FineReport——权限分配以及自定义首页
  8. Leetcode 之Longest Common Prefix(34)
  9. 提高C#编程水平的50个要诀
  10. AC日记——红色的幻想乡 洛谷 P3801