ejabberd分析(五)+订阅/添加好友

模块ejabberd_c2s中,状态为session_established2。

用户发送iq set 消息到服务器

<iq id="M8QF3-52" type="set"><query xmlns="jabber:iq:roster"><item jid="1555215557@kinglong" name="1555215557"><group>Friends</group></item></query></iq>

服务器端匹配到

[plain]  view plain copy print ?
  1. case Name of  
  2.     ......  
  3.     To = xml:get_attr_s("to", Attrs),  
  4.     ToJID = case To of  
  5.         "" ->  
  6.             jlib:make_jid(User, Server, "");  
  7.         _ ->  
  8.             jlib:string_to_jid(To)  
  9.         end,  
  10.          ......  
  11.          "iq" ->  
  12.              case jlib:iq_query_info(NewEl) of  
  13.                 #iq{xmlns = Xmlns} = IQ  
  14.                  ....  
  15.                  _ ->  
  16.                      ejabberd_hooks:run(  
  17.                          user_send_packet,  
  18.                          Server,  
  19.                          [FromJID, ToJID, NewEl]),  
  20.                          check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),  

上面这段代码需要注意ToJID的解析,发送给服务器的消息一般是默认不带to 属性的,这种情况下会自动将to 补上(这关系到包的路由)。

ejabberd_hooks:run(user_send_packet   会记录用户发送的消息到日志。

check_privacy_route 中对权限进行效验,通过后调用ejabberd_router:route(FromRoute, To, Packet) 进行路由。

在ejabberd_router:route中调用do_route/3:

[plain]  view plain copy print ?
  1. ejabberd_hooks:run_fold(filter_packet,  
先用包过滤模块进行过滤,通过后在路由表中查找路由到的模块的Pid:

[plain]  view plain copy print ?
  1. case mnesia:dirty_read(route, LDstDomain) of  

注:此处会以LDstDomain = To#jid.lserver, 去router表查找是交由本地处理,还是交由其他server 处理。

在模块ejabberd_local 中使用ejabberd_router:register_route(Host, {apply, ?MODULE, route}) 对当前domain进行了登记,所以会路由到此模块,并调用相应的route函数。

[plain]  view plain copy print ?
  1. mnesia:write(#route{domain = LDomain,  
  2.                             pid = Pid,  
  3.                             local_hint = LocalHint})  
  4.   
  5. {apply, Module, Function} ->  
  6.                     Module:Function(From, To, Packet);  
在ejabberd_local:route/3中调用do_route(From, To, Packet)。

客户端发送的iq消息初始to为空,但系统已自动补上ToJID(参见前面的说明),资源也为空的 iq,所以会路由到ejabberd_sm:route/3

[plain]  view plain copy print ?
  1.  if  
  2.     To#jid.luser /= "" ->  
  3.         ejabberd_sm:route(From, To, Packet);  

也就是说对于那些在session_established 之后发送的消息都会交由ejabberd_sm去路由。

在ejabberd_sm中由do_route/3 来处理:

[java]  view plain copy print ?
  1. do_route(From, To, Packet) ->  
  2.   case LResource of  
  3.     "" ->  
  4.          case Name of  
  5.            ......  
  6.            "iq" ->  
  7.             process_iq(From, To, Packet);  
ejabberd_sm 中有一张 sm_iqtable  里面记录了各个命名空间的消息最终由那个模块进行处理。

ejabberd_sm 是一个gen_server 当调用register_iq_handler 接口时会向sm_iqtable 插入记录。

而这些命名空间处理模块的登记是由gen_iq_handler 模块的add_iq_handler/6 来进行的。

[java]  view plain copy print ?
  1. -module(gen_iq_handler)  
  2.   
  3. add_iq_handler(Component, Host, NS, Module, Function, Type) ->  
  4.     case Type of  
  5.     no_queue ->  
  6.         Component:register_iq_handler(Host, NS, Module, Function, no_queue);  
jabber:iq:roster 定义的宏为NS_ROSTER,我们在源码目录下查找此命名空间是由哪个模块调用的注册:



在mod_roster:start/2 中有如下代码:

[java]  view plain copy print ?
  1. gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_ROSTER,  
  2.   ?MODULE, process_iq, IQDisc).  

可以看出mod_roster模块将自身的process_iq 函数在ejabberd_sm中注册为了jabber:iq:roster 命名空间的处理方法。

process_iq中针对发送到本地的iq调用process_local_iq/3:

[java]  view plain copy print ?
  1. process_local_iq(From, To, #iq{type = Type} = IQ) ->  
  2.     case Type of  
  3.     set ->  
  4.         process_iq_set(From, To, IQ);  
  5.     get ->  
  6.         process_iq_get(From, To, IQ)  
  7.     end.  

这里会对针对iq 的 type 进行不同处理,我们目前跟踪的是 <iq id="M8QF3-52" type="set">

[java]  view plain copy print ?
  1. process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->  
  2.     {xmlelement, _Name, _Attrs, Els} = SubEl,  
  3.     lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els),  
  4.     IQ#iq{type = result, sub_el = []}.  
 在process_iq_set 中会遍历每一item子项调用process_item_set/3 进行处理:
[java]  view plain copy print ?
  1. process_item_set(From, To, {xmlelement, _Name, Attrs, Els}) ->  
  2.     JID1 = jlib:string_to_jid(xml:get_attr_s("jid", Attrs)),  
  3.     #jid{user = User, luser = LUser, lserver = LServer} = From,  
  4.     case JID1 of  
  5.     error ->  
  6.         ok;  
  7.     _ ->  
  8.         JID = {JID1#jid.user, JID1#jid.server, JID1#jid.resource},  
  9.         LJID = jlib:jid_tolower(JID1),  
  10.         F = fun() ->  
  11.             Res = mnesia:read({roster, {LUser, LServer, LJID}}),  
  12.             Item = case Res of  
  13.                    [] ->  
  14.                        #roster{usj = {LUser, LServer, LJID},  
  15.                            us = {LUser, LServer},  
  16.                            jid = JID};  
  17.                    [I] ->  
  18.                        I#roster{jid = JID,  
  19.                         name = "",  
  20.                         groups = [],  
  21.                         xs = []}  
  22.                    end,  
  23.             Item1 = process_item_attrs(Item, Attrs),  
  24.             Item2 = process_item_els(Item1, Els),  
  25.                           
  26.                         case Item2#roster.subscription of              <pre name="code" class="java">                           %% 如果是删除  
  remove ->mnesia:delete({roster, {LUser, LServer, LJID}});
 %%否则添加
_ ->mnesia:write(Item2)end,
%% If the item exist in shared roster, take the%% subscription information from there:
Item3 = ejabberd_hooks:run_fold(roster_process_item,LServer, Item2, [LServer]), 
case roster_version_on_db(LServer) 
oftrue -> mnesia:write(#roster_version{us = {LUser, LServer}, version = sha:sha(term_to_binary(now()))});
false -> okend,{Item, Item3}end, 
case mnesia:transaction(F) 
of{atomic, {OldItem, Item}} -> push_item(User, LServer, To, Item), 
case Item#roster.subscription 
ofremove -> send_unsubscribing_presence(From, OldItem), 
ok;_ -> ok end;
E -> ?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok end end;
 

 

以上就是一条添加/订阅 好友的iq 消息在ejabberd 中从接收到最终写入到数据库所经过的路径。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值