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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值