ejabberd分析(一)

来源:互联网 发布:网络ip冲突是什么意思? 编辑:程序博客网 时间:2024/05/19 03:23

ejabberd分析(一)

1.客户端连接服务器,并发送消息给服务器的处理在ejabberd_c2s中。

   ejabberd_c2s 是一个gen_fsm 状态机,在 ejabberd启动时装载。

   初始状态为 wait_for_stream,接受形为 {xmlstreamstart, _Name, Attrs} 的消息,其他任何消息都会导致stop。

   接受到连接请求,发送feature 请求后状态改为wait_for_feature_request 。

   接受到feature 再发送challenge 后,状态改为 wait_for_sasl_response。

   经过sasl鉴权后,状态改为 wait_for_stream, 此时 StateData#state.authenticated 已经不为false。

   客户端重新发起<stream> 连接请求,服务器发送bind 消息,状态改为 wait_for_bind 。

   客户端发送bind 与资源绑定,服务器按照策略验证是否允许相同的用户用不同的资源连接,通过后状态改为 wait_for_session。

   客户端发送<iq> 消息创建session,服务器调用 ejabberd_sm:open_session 后将状态改为 session_established。

   客户端发送普通的通信消息<iq>,<presence>,<message> 等,都通过session_established2/2 函数处理。


[plain] view plaincopyprint?
  1. %% Process packets sent by user (coming from user on c2s XMPP  
  2. %% connection)  
  3. session_established2(El, StateData) ->  
  4.   
  5.     %%从变量El中取出Name,Attrs 两个参数,El必须是一个以xmlelement 为第一个元子的元组  
  6.     {xmlelement, Name, Attrs, _Els} = El,  
  7.     %%从StateDate中取出user,server,jid   
  8.     User = StateData#state.user,  
  9.     Server = StateData#state.server,  
  10.     FromJID = StateData#state.jid,  
  11.   
  12.     %%从Attrs属性变量中获取to(也就是发送给谁)  
  13.     To = xml:get_attr_s("to", Attrs),  
  14.        
  15.     %%将To转换成一个标准的JID:  
  16.     %%#jid{user,server,resource,luser,lserver,lresource},具体参见jlib.erl中的定义  
  17.     ToJID = case To of  
  18.         "" ->  
  19.             jlib:make_jid(User, Server, "");  
  20.         _ ->  
  21.             jlib:string_to_jid(To)  
  22.         end,  
  23.   
  24.     %%这里的El里的Attrs应该是一个元组组成的列表[{key1,value1},{key2,value2},{key3,value3},{key4,value4}.....]  
  25.     %%下面的语句会从Attrs中删除key 为xmlns的元组,并返回新的El存放到NewEl1中  
  26.     NewEl1 = jlib:remove_attr("xmlns", El),  
  27.     NewEl = case xml:get_attr_s("xml:lang", Attrs) of  
  28.         "" ->  
  29.             case StateData#state.lang of  
  30.             "" -> NewEl1;  
  31.             Lang ->  
  32.                 xml:replace_tag_attr("xml:lang", Lang, NewEl1)  
  33.             end;  
  34.         _ ->  
  35.             NewEl1  
  36.         end,  
  37.   
  38.     %%这里根据消息类型进行不同的处理  
  39.     NewState =  
  40.     case ToJID of  
  41.         error ->  
  42.         case xml:get_attr_s("type", Attrs) of  
  43.             "error" -> StateData;  
  44.             "result" -> StateData;  
  45.             _ ->  
  46.             Err = jlib:make_error_reply(NewEl, ?ERR_JID_MALFORMED),  
  47.             send_element(StateData, Err),  
  48.             StateData  
  49.         end;  
  50.         _ ->  
  51.         case Name of  
  52.             "presence" ->  
  53.                         %%如果为一个presence 消息,使用函数回调c2s_updatepresence参见2  
  54.                 PresenceEl = ejabberd_hooks:run_fold(  
  55.                        c2s_update_presence,  
  56.                        Server,  
  57.                        NewEl,  
  58.                        [User, Server]),  
  59.                  
  60.                           %%将调用结果发送回客户端  
  61.                           ejabberd_hooks:run(  
  62.               user_send_packet,  
  63.               Server,  
  64.               [FromJID, ToJID, PresenceEl]),  
  65.             case ToJID of  
  66.                 #jid{user = User,  
  67.                  server = Server,  
  68.                  resource = ""} ->  
  69.                 ?DEBUG("presence_update(~p,~n\t~p,~n\t~p)",  
  70.                        [FromJID, PresenceEl, StateData]),  
  71.                 presence_update(FromJID, PresenceEl,  
  72.                         StateData);  
  73.                 _ ->  
  74.                 presence_track(FromJID, ToJID, PresenceEl,  
  75.                            StateData)  
  76.             end;  
  77.             "iq" ->  
  78.                           %%iq 消息的处理.注册,添加好友等都是通过iq消息来发送的  
  79.                           case jlib:iq_query_info(NewEl) of  
  80.                 #iq{xmlns = Xmlns} = IQ  
  81.                 when Xmlns == ?NS_PRIVACY;  
  82.                  Xmlns == ?NS_BLOCKING ->  
  83.                 process_privacy_iq(  
  84.                   FromJID, ToJID, IQ, StateData);  
  85.                 _ ->  
  86.                 ejabberd_hooks:run(  
  87.                   user_send_packet,  
  88.                   Server,  
  89.                   [FromJID, ToJID, NewEl]),  
  90.                 check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),  
  91.                 StateData  
  92.             end;  
  93.             "message" ->  
  94.                         io:format("message arrvied~n",[]),  
  95.             ejabberd_hooks:run(user_send_packet,  
  96.                        Server,  
  97.                        [FromJID, ToJID, NewEl]),  
  98.             check_privacy_route(FromJID, StateData, FromJID,  
  99.                         ToJID, NewEl),  
  100.             StateData;  
  101.             _ ->  
  102.             StateData  
  103.         end  
  104.     end,  
  105.     ejabberd_hooks:run(c2s_loop_debug, [{xmlstreamelement, El}]),  
  106.     fsm_next_state(session_established, NewState).  


2.下面是比较关键的ejabberd中函数调用的分析:

[plain] view plaincopyprint?
  1.              run_fold(Hook, Host, Val, Args) ->  
  2.                   case ets:lookup(hooks, {Hook, Host}) of  
  3.                          [{_, Ls}] ->run_fold1(Ls, Hook, Val, Args);  
  4.                          [] ->Val  
  5.                   end.  
  6. e="code" class="plain">PresenceEl = ejabberd_hooks:run_fold(  
  7. c2s_update_presence,  
  8. Server,  
  9. NewEl,  
  10. [User, Server])  

在系统的ets库(内存中的一个数据库)中存有一个名为hooks的表,通过ets:lookup(hooks,{Hook,Host}) 可以找到一个 {_,Ls}的元组 (找不到就直接返回预定义的Val了),然后调用run_fold1(Ls,Hook,Val,Args).

Ls变量实际上是一个包含多个要具体调用的函数定义的列表,列表里面的元组分为两类:[{_Seq, Node,Moudle,Function} | Ls2]  [{_Seq,Module,Function} | Ls2],

run_fold1/4 的作用就是使用Args参数依次调用这个Ls列表里的方法.

run_fold1/4 最终会返回调用的结果出来.

所以从最终结果来看 ejabberd_hooks:run_fold/4  方法就是去表hooks查找并调用所需的函数返回调用结果.

[plain] view plaincopyprint?
  1. PresenceEl = ejabberd_hooks:run_fold(  
  2.                        c2s_update_presence,  
  3.                        Server,  
  4.                        NewEl,  
  5.                        [User, Server])  
针对上面的代码就是:

        使用{c2s_update_presence,Server}作为key 在表hooks 中查找 要调用的方法列表,并使用[User,Server] 作为参数进行调用.


这个key具体找到什么样的方法呢? 我们可以在源码中查找下:

[plain] view plaincopyprint?
  1. root@ubuntu:  grep *.erl -e c2s_update_presence  

查找结果中可以看到

mod_vcard_xupdate.erl               ejabberd_hooks:add(c2s_update_presence, Host


我们在mod_vcard_xupdate.erl中找到这段代码:

[plain] view plaincopyprint?
  1. start(Host, _Opts) ->  
  2.     mnesia:create_table(vcard_xupdate,  
  3.                         [{disc_copies, [node()]},  
  4.                          {attributes, record_info(fields, vcard_xupdate)}]),  
  5.     ejabberd_hooks:add(c2s_update_presence, Host,  
  6.                ?MODULE, update_presence, 100),  
  7.     ejabberd_hooks:add(vcard_set, Host,  
  8.                ?MODULE, vcard_set, 100),  
  9.     ok.  

update_presence就所我们所要找的方法了.

原创粉丝点击