Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Websocket 如何检测用户是否因网络断开而离开Phoenix频道?_Websocket_Elixir_Phoenix Framework_Channels - Fatal编程技术网

Websocket 如何检测用户是否因网络断开而离开Phoenix频道?

Websocket 如何检测用户是否因网络断开而离开Phoenix频道?,websocket,elixir,phoenix-framework,channels,Websocket,Elixir,Phoenix Framework,Channels,我有一个Elixir/Phoenix服务器应用程序,客户端通过WebSocket通过内置渠道系统进行连接。现在我想检测用户何时离开通道 旁注:我正在Google Chrome扩展中使用javascript客户端库。为此,我从Phoenix中提取了ES6代码,将其转换为javascript,并对其进行了一些调整,使其能够独立运行 现在,当我刚刚关闭弹出窗口时,服务器立即触发terminate/2函数,其中reason={:shutdown,:closed}。在扩展端不涉及任何类型的关闭回调,因此这

我有一个Elixir/Phoenix服务器应用程序,客户端通过WebSocket通过内置渠道系统进行连接。现在我想检测用户何时离开通道

旁注:我正在Google Chrome扩展中使用javascript客户端库。为此,我从Phoenix中提取了ES6代码,将其转换为javascript,并对其进行了一些调整,使其能够独立运行

现在,当我刚刚关闭弹出窗口时,服务器立即触发
terminate/2
函数,其中
reason={:shutdown,:closed}
。在扩展端不涉及任何类型的关闭回调,因此这非常棒

但是,当客户端只是松开网络连接(我连接了第二台计算机,刚刚拔出网络插头)时,
terminate/2
将不会触发

为什么以及如何修复此问题

我使用了
transport的
timeout
选项:websocket,Phoenix.Transports.websocket
,但没有成功

更新:
有了新的Aweasome Phoenix 1.2
Presence
功能,这应该不再需要了。

正确的方法是不要在通道中捕获出口,而是让另一个进程监视您。当你下线时,它可以调用回调。下面是一个让您开始学习的片段:

#lib/my_app.ex
儿童=[
...
工人(通道观察者,[:房间])
]
#web/channels/room_channel.ex
def join(“房间:”,id,参数,套接字)do
uid=套接字.分配.用户\u id]
:ok=ChannelWatcher.monitor(:rooms,self(),{{uuuuuu模块,:leave[id,uid]})
{:好的,socket}
结束
def离开(房间id、用户id)do
#处理用户离开
结束
#lib/my\u app/channel\u watcher.ex
defmodule ChannelWatcher do
使用GenServer
##客户端API
def监视器(服务器名称、pid、mfa)do
调用(服务器名称,{:monitor,pid,mfa})
结束
def demonitor(服务器名称,pid)do
调用(服务器名称,{:demonitor,pid})
结束
##服务器API
def start_链接(名称)do
GenServer.start_链接(_模块,[],名称:name)
结束
def init(u)do
Process.flag(:trap\u exit,true)
{:好,%{channels:HashDict.new()}
结束
def handle_调用({:monitor,pid,mfa},_from,state)do
进程链接(pid)
{:回复,:ok,输入_通道(状态,pid,mfa)}
结束
def handle_调用({:demonitor,pid},_from,state)do
case HashDict.fetch(state.channels,pid)do
:error->{:reply,:ok,state}
{:好的,{mfa}->
进程。取消链接(pid)
{:回复,:确定,删除_通道(状态,pid)}
结束
结束
def handle_info({:EXIT,pid,_reason},state)do
case HashDict.fetch(state.channels,pid)do
:error->{:noreply,state}
{:好,{mod,func,args}}->
任务开始链接(fn->apply(mod,func,args)结束)
{:noreply,drop_channel(state,pid)}
结束
结束
除雾下降通道(状态,pid)do
%{state | channels:HashDict.delete(state.channels,pid)}
结束
defp put_通道(状态、pid、mfa)do
%{state | channels:HashDict.put(state.channels,pid,mfa)}
结束
结束
在Elixir/Phoenix的较新版本中,HashDict已将名称更改为Map。 较新的代码基的正确示例是:

#lib/my_app.ex
儿童=[
...
工人(通道观察者,[:房间])
]
#web/channels/room_channel.ex
def join(“房间:”,id,参数,套接字)do
uid=套接字.分配.用户\u id]
:ok=ChannelWatcher.monitor(:rooms,self(),{{uuuuuu模块,:leave[id,uid]})
{:好的,socket}
结束
def离开(房间id、用户id)do
#处理用户离开
结束
#lib/my\u app/channel\u watcher.ex
defmodule ChannelWatcher do
使用GenServer
##客户端API
def监视器(服务器名称、pid、mfa)do
调用(服务器名称,{:monitor,pid,mfa})
结束
def demonitor(服务器名称,pid)do
调用(服务器名称,{:demonitor,pid})
结束
##服务器API
def start_链接(名称)do
GenServer.start_链接(_模块,[],名称:name)
结束
def init(u)do
Process.flag(:trap\u exit,true)
{:好,%{channels:Map.new()}
结束
def handle_调用({:monitor,pid,mfa},_from,state)do
进程链接(pid)
{:回复,:ok,输入_通道(状态,pid,mfa)}
结束
def handle_调用({:demonitor,pid},_from,state)do
case Map.fetch(state.channels,pid)do
:error->{:reply,:ok,state}
{:好的,{mfa}->
进程。取消链接(pid)
{:回复,:确定,删除_通道(状态,pid)}
结束
结束
def handle_info({:EXIT,pid,_reason},state)do
case Map.fetch(state.channels,pid)do
:error->{:noreply,state}
{:好,{mod,func,args}}->
任务开始链接(fn->apply(mod,func,args)结束)
{:noreply,drop_channel(state,pid)}
结束
结束
除雾下降通道(状态,pid)do
%{state | channels:Map.delete(state.channels,pid)}
结束
defp put_通道(状态、pid、mfa)do
%{state | channels:Map.put(state.channels,pid,mfa)}
结束
结束

我刚刚注意到服务器并不总是能够识别弹出窗口何时关闭。因此,我希望我的问题的解决方案也能解决这个问题。请注意,当最后一个订阅频道的用户失去连接时,状态将不起作用。看,这可能不是正确的解决方案。也看,很明显,即使存在,您仍然需要一个外部进程来监控频道,因此这里的答案仍然非常相关。我让它工作,我有两个后续问题:1。为什么这个不在核心(太好了)?2.网络断开和离开触发之间的时间大约为90秒。这是可以定制的吗?(我曾想过将传输超时设置为20秒,并每10秒ping一次服务器…当然,额外的资源会被消耗掉)这个解决方案非常棒。唯一的问题是我在
leave
函数中执行了一些DB操作,这个问题在测试中发生。很好,Elixir v1.8.0和DBConnection v2.0.4最终解决了这个问题