ejabberd分析(一)

本文详细介绍了ejabberd中客户端连接服务器的过程,包括xmlstreamstart消息处理、特征请求、SASL鉴权、绑定与资源绑定、session建立等关键步骤,以及客户端发送不同类型消息的处理逻辑。
摘要由CSDN通过智能技术生成

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 函数处理。


%% Process packets sent by user (coming from user on c2s XMPP
%% connection)
session_established2(El, StateData) ->

    %%从变量El中取出Name,Attrs 两个参数,El必须是一个以xmlelement 为第一个元子的元组
    {xmlelement, Name, Attrs, _Els} = El,
    %%从StateDate中取出user,server,jid 
    User = StateData#state.user,
    Server = StateData#state.server,
    FromJID = StateData#state.jid,

    %%从Attrs属性变量中获取to(也就是发送给谁)
    To = xml:get_attr_s("to", Attrs),
     
    %%将To转换成一个标准的JID:
    %%#jid{user,server,resource,luser,lserver,lresource},具体参见jlib.erl中的定义
    ToJID = case To of
		"" ->
		    jlib:make_jid(User, Server, "");
		_ ->
		    jlib:string_to_jid(To)
	    end,

    %%这里的El里的Attrs应该是一个元组组成的列表[{key1,value1},{key2,value2},{key3,value3},{key4,value4}.....]
    %%下面的语句会从Attrs中删除key 为xmlns的元组,并返回新的El存放到NewEl1中
    NewEl1 = jlib:remove_attr("xmlns", El),
    NewEl = case xml:get_attr_s("xml:lang", Attrs) of
		"" ->
		    case StateData#state.lang of
			"" -> NewEl1;
			Lang ->
			    xml:replace_tag_attr("xml:lang", Lang, NewEl1)
		    end;
		_ ->
		    NewEl1
	    end,

    %%这里根据消息类型进行不同的处理
    NewState =
	case ToJID of
	    error ->
		case xml:get_attr_s("type", Attrs) of
		    "error" -> StateData;
		    "result" -> StateData;
		    _ ->
			Err = jlib:make_error_reply(NewEl, ?ERR_JID_MALFORMED),
			send_element(StateData, Err),
			StateData
		end;
	    _ ->
		case Name of
		    "presence" ->
                        %%如果为一个presence 消息,使用函数回调c2s_updatepresence参见2
       			PresenceEl = ejabberd_hooks:run_fold(
				       c2s_update_presence,
				       Server,
				       NewEl,
				       [User, Server]),
		       
                          %%将调用结果发送回客户端
                          ejabberd_hooks:run(
			  user_send_packet,
			  Server,
			  [FromJID, ToJID, PresenceEl]),
			case ToJID of
			    #jid{user = User,
				 server = Server,
				 resource = ""} ->
				?DEBUG("presence_update(~p,~n\t~p,~n\t~p)",
				       [FromJID, PresenceEl, StateData]),
				presence_update(FromJID, PresenceEl,
						StateData);
			    _ ->
				presence_track(FromJID, ToJID, PresenceEl,
					       StateData)
			end;
		    "iq" ->
                          %%iq 消息的处理.注册,添加好友等都是通过iq消息来发送的
                          case jlib:iq_query_info(NewEl) of
			    #iq{xmlns = Xmlns} = IQ
			    when Xmlns == ?NS_PRIVACY;
				 Xmlns == ?NS_BLOCKING ->
				process_privacy_iq(
				  FromJID, ToJID, IQ, StateData);
			    _ ->
				ejabberd_hooks:run(
				  user_send_packet,
				  Server,
				  [FromJID, ToJID, NewEl]),
				check_privacy_route(FromJID, StateData, FromJID, ToJID, NewEl),
				StateData
			end;
		    "message" ->
                        io:format("message arrvied~n",[]),
			ejabberd_hooks:run(user_send_packet,
					   Server,
					   [FromJID, ToJID, NewEl]),
			check_privacy_route(FromJID, StateData, FromJID,
					    ToJID, NewEl),
			StateData;
		    _ ->
			StateData
		end
	end,
    ejabberd_hooks:run(c2s_loop_debug, [{xmlstreamelement, El}]),
    fsm_next_state(session_established, NewState).


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

                        run_fold(Hook, Host, Val, Args) ->
                             case ets:lookup(hooks, {Hook, Host}) of
                                    [{_, Ls}] ->run_fold1(Ls, Hook, Val, Args);
                                    [] ->Val
                             end.
PresenceEl = ejabberd_hooks:run_fold(
				       c2s_update_presence,
				       Server,
				       NewEl,
				       [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查找并调用所需的函数返回调用结果.

PresenceEl = ejabberd_hooks:run_fold(
				       c2s_update_presence,
				       Server,
				       NewEl,
				       [User, Server])
针对上面的代码就是:

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


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

root@ubuntu:  grep *.erl -e c2s_update_presence

查找结果中可以看到

mod_vcard_xupdate.erl               ejabberd_hooks:add(c2s_update_presence, Host


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

start(Host, _Opts) ->
    mnesia:create_table(vcard_xupdate,
                        [{disc_copies, [node()]},
                         {attributes, record_info(fields, vcard_xupdate)}]),
    ejabberd_hooks:add(c2s_update_presence, Host,
		       ?MODULE, update_presence, 100),
    ejabberd_hooks:add(vcard_set, Host,
		       ?MODULE, vcard_set, 100),
    ok.

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






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值