Joh*_*ter 23 erlang xmpp ejabberd
我有ejabberd设置为移动应用程序之间的xmpp服务器,即.自定义iPhone和Android应用.
但我似乎陷入了对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响应?如果没有响应,则将其保存为脱机.
是否可以编写一个钩子来解决这个问题?
或者是否有一个我错过的简单设置?
仅供参考:我没有使用BOSH.
Joh*_*ter 11
这是我写的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).
Run Code Online (Sandbox Code Playgroud)
这是众所周知的TCP连接限制.您需要引入一些确认功能.
xep-0184中的一个选项.消息可以携带收据请求,并且当它被递送时,收据将返回给发件人.
另一种选择是xep-0198.这是流管理,承认节.
您还可以在应用程序层中完全实现它,并将邮件从收件人发送到发件人.当确认未送达时,相应地采取行动.请注意,发件人 - >服务器连接也可能以这种方式被切断.
我不知道ejabberd中那些xeps和功能的实现.我根据项目要求自己实现它们.
| 归档时间: |
|
| 查看次数: |
7964 次 |
| 最近记录: |