Erlang 聊天室程序(八) 主题房间---supervisor 的使用

之前实现的功能相当于一个大厅,这里我们为这个聊天室程序添加“主题房间的功能”。客户端登陆后处于大厅中,然后获取目前所有的主题房间,再发送消息进入到某个房间内。同一房间的成员可见,发送的消息也只限本房间可见。

为了管理这些房间,Erlang Opt中的监控树是最合适的。

新建一个模块room_manager.erl 其行为遵守gen_supervisor,子进程为chat_room

init(Para)-> {ok, { {one_for_one,3,5}, [ {the_room, {chat_room,start_link,[]}, permanent, 10000, worker } ] } } .

再添加一个方法用于启动这个supervisor

start_link(Para)-> supervisor:start_link({local,?MODULE},?MODULE, [Para]) .
如果现在就直接启动这个模块是没有问题的,但既然是主题房间,肯定不止一个吧。下面就为这个supervisor添加再启动主题房间的功能:startNewRoom(Para)-> supervisor:start_child(the_room, [Para]) .

就这么简单? 肯定不是。还有以下几点需要考虑:

1.如何标识每个房间的信息,如名称、类型、编号、启动时间、当前用户数等。

2.如何维护每个房间的在线用户信息。

3.如何维护当前可用的主题房间的信息。

4.如何从大厅进入房间。

5.如何从房间退出到大厅。

6.如何将信息路由到自己所在的房间。

下面就一个一个地解决:

房间的标识:

如上代码启动主题房间的时候传入了Para参数,可以通过这个参数来初始化任何你需要的房间信息。PS: 这就是Erlang 的好处之一啊大笑

修改chat_room.erl

start_link(Para)-> gen_server:start_link(?MODULE, [Para],[]).

定义一个record叫roominfo

-record(roominfo,{id,name,type,unum,creationDate}).

用传入进来的参数初始化房间信息

init([Para])-> id_generator:start_link(), %ets:new(clientinfo,[public, % ordered_set, % named_table, % {keypos,#clientinfo.id} % ]), client_manager:init(), RoomId=id_generator:getnewid("room"), case is_record(Para,roominfo)of true-> {ok,#roominfo{unum=0}=Para}; Els-> %maybe we should throw exception here {ok,#roominfo{id=RoomId,unum=0,name="room"++integer_to_list(RoomId),type="def"}} end .
每个房间的在线成员信息维护:

还记得client_manager这个模块否?这个模块就是用来专门管理房间对应的在线成员的,这里需要对其进行修改。

在前面的代码中client_manager这个模块的调用是与chat_room处于同一进程内的,也就是顺序性调用。

每次调用时所操纵的表是写死的,在这里需要改为动态的,在每次操纵时传入要操纵的表。

所以应该在chat_room中保存自己拥有的表名。

修改roominfo的定义添加tablename,并在初始化chat_room时生成好。

-record(roominfo,{id,name,type,unum,tablename,creationDate}).init([Para])-> id_generator:start_link(), %ets:new(clientinfo,[public, % ordered_set, % named_table, % {keypos,#clientinfo.id} % ]), RoomId=id_generator:getnewid("room"), ClientTableName=list_to_atom(?PRIFIX++integer_to_list(RoomId)), client_manager:init(ClientTableName), case is_record(Para,roominfo)of true-> {ok,#roominfo{unum=0}=Para}; Els-> {ok,#roominfo{id=RoomId,unum=0,name="room"++integer_to_list(RoomId),type="def"}} end .

修改client_manger.erl为每个方法都添加table参数。

%% Author: Administrator %% Created: 2012-2-28 %% Description: TODO: response for managing clientinfo -module(client_manager). %% %% Include files %% -include("clientinfo.hrl"). -include("message.hrl"). %% %% Exported Functions %% -export([init/1,addClient/2,updateClient/2,getNextClient/2,removeClient/2,getClient/2,getNick/2]). %% %% API Functions %% %%create clientinfo talbe here init(Tab)-> ets:new(Tab,[public, ordered_set, named_table, {keypos,#clientinfo.id} ]) . %% %% Local Functions %% %add new clientinfo addClient(Tab,ClientInfo)-> ets:insert(Tab, ClientInfo) . %update clientinfo updateClient(Tab,Message)-> #message{content=Info,from=Key}=Message, io:format("content of message is:~p~n",[Info]), %TODO:we should formart content from json to record #clientinfo Rec =util_SetInfoParas:paraElements(Info), io:format("parased record is:~p~n",[Rec]), if is_record(Rec,clientinfo) -> #clientinfo{nick=Nick,sex=Sex,age=Age,province=Province, city=City,posx=Px,posy=Py}=Rec, io:format("here Key is:~p~n",[Key]), case ets:update_element(Tab, Key, [{#clientinfo.nick,Nick}, {#clientinfo.sex,Sex}, {#clientinfo.age,Age}, {#clientinfo.province,Province}, {#clientinfo.city,City}, {#clientinfo.posx,Px}, {#clientinfo.posy,Py}]) of true-> {ok,Rec}; false-> {fals,Rec} end; true-> io:format("wrong format of clientinfo"), {false,Rec} end . %remove clientinfo removeClient(Tab,Key)-> [TheKey]=Key, case ets:lookup(Tab, TheKey) of [Record]-> io:format("record tobe delete from clientinfo is:~p~n",[Record]); []-> io:format("no record found tobe delete from clientinfo~n") end, io:format("delete clientinfo from table key is:~p~n",[TheKey]), ets:delete(Tab, TheKey) . getNick(Tab,Key)-> case ets:lookup(Tab, Key) of [Record]-> #clientinfo{nick=Nick}=Record, case Nick of undefined -> "client"++integer_to_list(Key); Nick-> Nick end; []-> "client"++integer_to_list(Key) end . getClient(Tab,Key)-> ets:lookup(Tab, Key) . getNextClient(Tab,[Key])-> case ets:next(Tab, Key) of '$end_of_table'-> io:format("getNext1,no key found~n"), []; Next-> io:format("getNext1,key found~p~n",[Key]), ets:lookup(Tab, Next) end ; getNextClient(Tab,[])-> case ets:first(Tab) of '$end_of_table'-> io:format("getNext,no key found~n"), []; Key-> io:format("getNext,key found~p~n",[Key]), ets:lookup(Tab, Key) end .
再修改chat_room.erl中调用部分,为每个调用都添加table参数。

 

handle_call({remove_clientinfo,Ref},From,State)-> Key=Ref#clientinfo.id, #roominfo{tablename=Table}=State, client_manager:removeClient(Table,Key), {reply,ok,State} ; handle_call({sendmsg,Msg},From,State)-> %Key=ets:first(clientinfo), %io:format("feching talbe key is ~p~n",[Key]), %sendMsg(Key,Msg), #roominfo{tablename=Table}=State, sendMsg(Table,Msg,[]), {reply,ok,State} ; handle_call({get_member,Msg},From,State)-> #message{content=Content}=Msg, #roominfo{tablename=Table}=State, case binary_to_list(Content) of "all"-> List=getClient(Table,[],[]), #message{from=Id}=Msg, TheFrom=client_manager:getNick(Table,Id), Message=Msg#message{type="result",from=TheFrom,subject="clientinfo",content=List}, {Pid,_}=From, Pid!{get_member,Message}; _Els-> ok end, {reply,ok,State} ; handle_call({add_clientinfo,ClientInfo},From,State)-> %store clientinfo to ets %ets:insert(clientinfo, NewRec) #roominfo{tablename=Table}=State, client_manager:addClient(Table,ClientInfo), {reply,ok,State} ; handle_call({set_clientinfo,Message},From,State)-> %we can send result message to client %or send a broadcast message to all client #message{from=Id}=Message, #roominfo{tablename=Table}=State, [Client]=client_manager:getClient(Table,Id), #clientinfo{pid=Pid}=Client, TheNick=client_manager:getNick(Table,Id), case client_manager:updateClient(Table,Message)of {ok,Rec}-> #clientinfo{nick=Nick}=Rec, Pid!{setinfo,Rec}, {Content}={TheNick ++ " has change nickname:" ++ Nick}, NewMessage=#message{id="0", from=Id, to="", content=Content, type="msg", subject="chat", time=Message#message.time}, io:format("Notice Message is:~p~n",[NewMessage]), sendMsg(Table,NewMessage,[]); {false,Rec}-> ok end, {reply,ok,State} . sendMsg(Table,Msg,Key)-> io:format("broading casting msg~p~n",[Key]), case client_manager:getNextClient(Table,Key)of [Record]-> Next=Record#clientinfo.id, Pid=Record#clientinfo.pid, Pid!{dwmsg,Msg}, sendMsg(Table,Msg,[Next]); []-> ok end . getClient(Table,Key,UList)-> case client_manager:getNextClient(Key) of [Record]-> Next=Record#clientinfo.id, io:format("record is:~p~n",[Record]), Json=util_SetInfoParas:deparaElement(Record), io:format("clientSession found:~p~n",[Record]), NewUList=[Json|UList], getClient(Table,[Next],NewUList); []-> UList end .

如此即可。注:client_manager有一个getNick/1 函数,负责返回对应ID用户的昵称。如何从正确的表格中找出用户的昵称?这个问题将留在用户消息路由部分去处理。

暂时到这。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值