当用户失去连接时,ejabberd在线状态 [英] ejabberd online status when user loses connection

查看:103
本文介绍了当用户失去连接时,ejabberd在线状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有ejabberd设置为移动应用程序之间的xmpp服务器,即。自定义iPhone和Android应用程序。

I have ejabberd setup to be the xmpp server between mobile apps, ie. custom iPhone and Android app.

但是我似乎遇到了ejabberd处理在线状态的限制。

But I've seemingly run into a limitation of the way ejabberd handles online status's.

情景:


  • 用户A通过手机发送用户B.

  • 用户B失去所有的连接,所以客户端不能断开与服务器的连接。

  • ejabberd仍然列出用户B在线。

  • 由于ejabberd假定用户B仍然在线,用户A的任何消息都会传递给死者连接。

  • 所以用户B不会收到消息,也不会被保存为离线消息,如ejabberd假定用户在线。

  • 消息丢失。

  • 直到ejabberd意识到连接过时,它将其视为在线用户。

  • User A is messaging User B via their mobiles.
  • User B loses all connectivity, so client can't disconnect from server.
  • ejabberd still lists User B as online.
  • Since ejabberd assumes User B is still online, any message from User A gets passed on to the dead connection.
  • So user B won't get the message, nor does it get saved as an offline message, as ejabberd assumes the user is online.
  • Message lost.
  • Until ejabberd realises that the connection is stale, it treats it as an online user.

将数据连接更改(wifi到3G到4G),你会发现这很多。

And throw in data connection changes (wifi to 3G to 4G to...) and you'll find this happening quite a lot.

mod_ping:

我试图在10秒的时间内实现mod_ping。

https://www.process-one.net/docs/ejabberd/guide_en.html#modping

但正如文档所述,ping将等待32断开用户之前响应的秒数。

这意味着将有一个42秒的窗口,用户可以丢失他们的消息。

I tried to implement mod_ping on a 10 second interval.
https://www.process-one.net/docs/ejabberd/guide_en.html#modping
But as the documentation states, the ping will wait 32 seconds for a response before disconnecting the user.
This means there will be a 42 second window where the user can lose their messages.

理想的解决方案:

即使ping等待时间可以减少,仍然不是一个完美的解决方案。

是否有一种方式,ejabberd可以在丢弃消息之前等待客户端的200响应?如果没有响应,则将其保存为离线。

是否可以编写一个钩子来解决这个问题?

还是有一个简单的设置我错过了某个地方?

Even if the ping wait time could be reduce, it's still not a perfect solution.
Is there a way that ejabberd can wait for a 200 response from the client before discarding the message? If no response then save it offline.
Is it possible to write a hook to solve this problem?
Or is there a simple setting I've missed somewhere?

FYI:我没有使用BOSH。

FYI: I am not using BOSH.

推荐答案

这是我写的mod修复了我的问题。

Here is the mod I wrote that fixes my problem.

为了使其发挥作用,您需要收到客户端的收据,客户端应该能够处理重复的邮件。

To make it work you'll need receipts to be activated client side and the client should be able to handle duplicate messages.

首先我创建了一个名为confirm_delivery的表。我保存每个聊天消息到该表。我设置了一个10秒的计时器,如果我收到一个确认回来,我删除表条目。

Firstly I created a table called confirm_delivery. I save every 'chat' message to that table. I set a 10 second timer, if I receive a confirmation back, I delete the table entry.

如果我没有得到确认,我将手动将消息保存到offline_msg表,然后再次尝试重新发送(这可能在顶部,但是您决定),然后从我们的confirm_delivery表中删除

If I don't get a confirmation back, I save the message manually to the offline_msg table and try and resend it again (this might be over the top, but for you to decide) and then delete it from our confirm_delivery table

我已经将所有我认为的代码都切成不必要的,所以希望这样仍然可以编译。

I've chopped out all the code I perceive as unnecessary, so I hope this will still compile.

希望这对其他ejabberd开发人员有帮助!

Hope this is of help to other ejabberd devs out there!

https://github.com/johanvorster/ejabberd_confirm_delivery.git

%% name of module must match file name
-module(mod_confirm_delivery).

-author("Johan Vorster").

%% Every ejabberd module implements the gen_mod behavior
%% The gen_mod behavior requires two functions: start/2 and stop/1
-behaviour(gen_mod).

%% public methods for this module
-export([start/2, stop/1, send_packet/3, receive_packet/4, get_session/5, set_offline_message/5]).

%% included for writing to ejabberd log file
-include("ejabberd.hrl").

-record(session, {sid, usr, us, priority, info}).
-record(offline_msg, {us, timestamp, expire, from, to, packet}).

-record(confirm_delivery, {messageid, timerref}).

start(_Host, _Opt) -> 

        ?INFO_MSG("mod_confirm_delivery loading", []),
        mnesia:create_table(confirm_delivery, 
            [{attributes, record_info(fields, confirm_delivery)}]),
        mnesia:clear_table(confirm_delivery),
        ?INFO_MSG("created timer ref table", []),

        ?INFO_MSG("start user_send_packet hook", []),
        ejabberd_hooks:add(user_send_packet, _Host, ?MODULE, send_packet, 50),   
        ?INFO_MSG("start user_receive_packet hook", []),
        ejabberd_hooks:add(user_receive_packet, _Host, ?MODULE, receive_packet, 50).   

stop(_Host) -> 
        ?INFO_MSG("stopping mod_confirm_delivery", []),
        ejabberd_hooks:delete(user_send_packet, _Host, ?MODULE, send_packet, 50),
        ejabberd_hooks:delete(user_receive_packet, _Host, ?MODULE, receive_packet, 50). 

send_packet(From, To, Packet) ->    
    ?INFO_MSG("send_packet FromJID ~p ToJID ~p Packet ~p~n",[From, To, Packet]),

    Type = xml:get_tag_attr_s("type", Packet),
    ?INFO_MSG("Message Type ~p~n",[Type]),

    Body = xml:get_path_s(Packet, [{elem, "body"}, cdata]), 
    ?INFO_MSG("Message Body ~p~n",[Body]),

    MessageId = xml:get_tag_attr_s("id", Packet),
    ?INFO_MSG("send_packet MessageId ~p~n",[MessageId]), 

    LUser = element(2, To),
    ?INFO_MSG("send_packet LUser ~p~n",[LUser]), 

    LServer = element(3, To), 
    ?INFO_MSG("send_packet LServer ~p~n",[LServer]), 

    Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
    ?INFO_MSG("Session: ~p~n",[Sessions]),

    case Type =:= "chat" andalso Body =/= [] andalso Sessions =/= [] of
        true ->                

        {ok, Ref} = timer:apply_after(10000, mod_confirm_delivery, get_session, [LUser, LServer, From, To, Packet]),

        ?INFO_MSG("Saving To ~p Ref ~p~n",[MessageId, Ref]),

        F = fun() ->
            mnesia:write(#confirm_delivery{messageid=MessageId, timerref=Ref})
        end,

        mnesia:transaction(F);

    _ ->
        ok
    end.   

receive_packet(_JID, From, To, Packet) ->
    ?INFO_MSG("receive_packet JID: ~p From: ~p To: ~p Packet: ~p~n",[_JID, From, To, Packet]), 

    Received = xml:get_subtag(Packet, "received"), 
    ?INFO_MSG("receive_packet Received Tag ~p~n",[Received]),    

    if Received =/= false andalso Received =/= [] ->
        MessageId = xml:get_tag_attr_s("id", Received),
        ?INFO_MSG("receive_packet MessageId ~p~n",[MessageId]);       
    true ->
        MessageId = []
    end, 

    if MessageId =/= [] ->
        Record = mnesia:dirty_read(confirm_delivery, MessageId),
        ?INFO_MSG("receive_packet Record: ~p~n",[Record]);       
    true ->
        Record = []
    end, 

    if Record =/= [] ->
        [R] = Record,
        ?INFO_MSG("receive_packet Record Elements ~p~n",[R]), 

        Ref = element(3, R),

        ?INFO_MSG("receive_packet Cancel Timer ~p~n",[Ref]), 
        timer:cancel(Ref),

        mnesia:dirty_delete(confirm_delivery, MessageId),
        ?INFO_MSG("confirm_delivery clean up",[]);     
    true ->
        ok
    end.


get_session(User, Server, From, To, Packet) ->   
    ?INFO_MSG("get_session User: ~p Server: ~p From: ~p To ~p Packet ~p~n",[User, Server, From, To, Packet]),   

    ejabberd_router:route(From, To, Packet),
    ?INFO_MSG("Resend message",[]),

    set_offline_message(User, Server, From, To, Packet),
    ?INFO_MSG("Set offline message",[]),

    MessageId = xml:get_tag_attr_s("id", Packet), 
    ?INFO_MSG("get_session MessageId ~p~n",[MessageId]),    

    case MessageId =/= [] of
        true ->        

        mnesia:dirty_delete(confirm_delivery, MessageId),
        ?INFO_MSG("confirm_delivery clean up",[]);

     _ ->
        ok
    end.

set_offline_message(User, Server, From, To, Packet) ->
    ?INFO_MSG("set_offline_message User: ~p Server: ~p From: ~p To ~p Packet ~p~n",[User, Server, From, To, Packet]),    

    F = fun() ->
        mnesia:write(#offline_msg{us = {User, Server}, timestamp = now(), expire = "never", from = From, to = To, packet = Packet})
    end,

    mnesia:transaction(F).    

这篇关于当用户失去连接时,ejabberd在线状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆