ejabberd statut en ligne lorsque l'utilisateur perd la connexion

j'ai ejabberd setup pour être le serveur xmpp entre les applications mobiles, c'est à dire. application iPhone et Android personnalisées.

mais j'ai apparemment rencontré une limitation de la façon dont ejabberd gère le statut en ligne.

Scénario:

  • L'utilisateur A envoie un message à l'utilisateur B par l'intermédiaire de ses mobiles.
  • L'utilisateur B perd toute connectivité, donc le client ne peut pas se déconnecter du serveur.
  • ejabberd affiche toujours L'utilisateur B comme étant en ligne.
  • depuis ejabberd suppose utilisateur B est toujours en ligne, tout message de L'utilisateur A est transmis à la connexion morte.
  • Si l'utilisateur B n'aurez pas de message, ni ne sont enregistrés comme un message hors ligne, comme ejabberd suppose que l'utilisateur est en ligne.
  • message perdu.
  • Jusqu'à ce qu'ejabberd réalise que la connexion est périmée, il la traite comme un utilisateur en ligne.

Et de le jeter dans les données de changement de connexion (wifi, de la 3G à la 4G...) et vous trouverez ce qui se passe tout à fait un beaucoup.

mod_ping:

j'ai essayé d'implémenter mod_ping sur 10 Secondes interval.

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

mais comme le dit la documentation, le ping attendra 32 secondes pour une réponse avant de déconnecter l'utilisateur.

Cela signifie qu'il y aura un 42 deuxième fenêtre où l'utilisateur peut perdre leurs messages.

Solution Idéale:

Même si le ping le temps d'attente pourrait être réduit, ce n'est toujours pas une solution parfaite.

existe-t-il un moyen pour qu'ejabberd puisse attendre une réponse de 200 de la part du client avant de rejeter le message? Si vous ne répondez pas, sauvegardez-le hors ligne.

Est-il possible d'écrire un crochet pour résoudre ce problème?

ou y a-t-il un décor simple que j'ai manqué quelque part?

pour info: je n'utilise pas BOSH.

23
demandé sur Johan Vorster 2013-07-02 15:21:51

6 réponses

voici le mod que j'ai écrit qui résout mon problème.

pour que cela fonctionne, vous aurez besoin de reçus pour être activé côté client et le client devrait être en mesure de gérer les messages en double.

tout d'abord j'ai créé une table appelée confirm_delivery. J'enregistre tous les messages à cette table. Je mets une minuterie de 10 secondes, si je reçois une confirmation en arrière, je supprime l'entrée de table.

si je n'ai pas de confirmation, je sauvegarde le message manuellement sur offline_msg table et essayer et la renvoyer à nouveau (cela pourrait être au-dessus du dessus, mais pour vous de décider) et puis le supprimer de notre confirm_delivery table

j'ai coupé tout le code que je perçois comme inutile, donc j'espère que cela va encore se compiler.

J'espère que ceci est d'une aide pour d'autres devs ejabberd là-bas!

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).    
10
répondu Johan Vorster 2014-04-07 09:51:17

c'est une limitation bien connue des connexions TCP. Vous devez introduire une certaine fonctionnalité de reconnaissance.

l'Une des options dans la xep-0184. Un message peut contenir une demande de récépissé et, lorsqu'il est livré, le récépissé retourne à l'expéditeur.

une autre option est xep-0198. Il s'agit de stream management qui reconnaît stanzas.

vous pouvez également l'implémenter entièrement dans la couche application et envoyer des messages du destinataire à l'expéditeur. Agir en conséquence lors de accusé de réception n'est pas livré. Rappelez-vous que la connexion Sender - > Server peut aussi être coupée de cette façon.

Je ne suis pas au courant de l'implémentation de ces xeps et fonctionnalités dans ejabberd. Je les ai mis en œuvre moi-même en fonction des exigences du projet.

5
répondu user425720 2013-07-02 14:03:05

ejabberd prend en charge la gestion des flux par défaut dans la dernière version. Il est implémenté dans la plupart des bibliothèques mobiles comme Smack pour Android et Xmppfram Framework pour iOS.

C'est l'état de l'art dans la spécification XMPP en ce moment.

2
répondu Mickaël Rémond 2015-07-22 10:46:29

la mise en oeuvre de XEP-198 sur ejabberd est très impliquée.

Erlang Solutions (je travaille pour eux) dispose d'un module XEP-184 pour ejabberd, avec des fonctionnalités améliorées, qui résout ce problème. Il ne le tampon et la validation côté serveur. Aussi longtemps que le client envoie des messages portant la demande de réception et quand il est livré, le reçu retourne à l'expéditeur.

le module valide les reçus pour voir si le message a été reçu. Si elle n'a pas à l'intérieur du délai d'attente, il est enregistré comme un message hors ligne.

1
répondu steve-erlang 2013-12-09 13:32:17

je pense que le meilleur moyen est que si un message n'a pas été reçu, faire l'utilisateur hors ligne, puis stocker le message dans la table de message hors ligne et utiliser un service push et le configurer pour le message hors ligne.

alors une poussée sera envoyée et s'il y a plus de message ils seront stockés sur le message hors ligne, et pour comprendre sur le serveur que le message n'a pas reçu vous pouvez utiliser ceci https://github.com/Mingism/ejabberd-stanza-ack.

je pense que Facebook a l' de la même manière, lorsqu'un message ne parvient pas à être livré, il rend l'utilisateur déconnecté jusqu'à ce qu'il redevienne en ligne

1
répondu user3503159 2014-04-06 09:16:40

Ejabberd prend en charge la gestion des flux par défaut dans la dernière version. Après avoir défini la configuration de stream manager dans ejabberd_c2s, vous devez définir une certaine configuration dans votre client. Veuillez voir ce post pour cette config client. https://community.igniterealtime.org/thread/55715

0
répondu M.Rezaei 2016-08-28 07:17:09