gpt4 book ai didi

erlang - 用户断开连接时的 ejabberd 在线状态

转载 作者:行者123 更新时间:2023-12-03 11:28:40 25 4
gpt4 key购买 nike

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

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

设想:

  • 用户 A 正在通过他们的手机向用户 B 发送消息。
  • 用户 B 失去所有连接,因此客户端无法与服务器断开连接。
  • ejabberd 仍将用户 B 列为在线。
  • 由于 ejabberd 假定用户 B 仍然在线,因此来自用户 A 的任何消息都会传递到死连接。
  • 因此,用户 B 不会收到消息,也不会将其保存为离线消息,因为 ejabberd 假定用户在线。
  • 消息丢失。
  • 直到 ejabberd 意识到连接已过时,它才会将其视为在线用户。

  • 再加上数据连接的变化(wifi 到 3G 到 4G 到……),你会发现这种情况经常发生。

    mod_ping:

    我尝试以 10 秒的间隔实现 mod_ping。
    https://www.process-one.net/docs/ejabberd/guide_en.html#modping
    但正如文档所述,ping 将在断开用户连接之前等待 32 秒的响应。
    这意味着将有一个 42 秒的窗口,用户可能会丢失他们的消息。

    理想解决方案:

    即使可以减少 ping 等待时间,它仍然不是一个完美的解决方案。
    有没有办法让 ejabberd 在丢弃消息之前等待来自客户端的 200 响应?如果没有响应,则离线保存。
    是否可以编写一个钩子(Hook)来解决这个问题?
    还是我在某个地方错过了一个简单的设置?

    仅供引用:我没有使用 BOSH。

    最佳答案

    这是我写的解决我的问题的mod。

    为了使其工作,您需要在客户端激活收据,并且客户端应该能够处理重复的消息。

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

    如果我没有收到确认回复,我会手动将消息保存到 offline_msg 表并尝试重新发送(这可能超出顶部,但由您决定),然后将其从我们的 confirm_delivery 表中删除

    我已经删除了所有我认为不必要的代码,所以我希望它仍然可以编译。

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

    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).

    关于erlang - 用户断开连接时的 ejabberd 在线状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17424254/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com