gpt4 book ai didi

websocket - 如何检测用户是否因网络断开而离开 Phoenix channel ?

转载 作者:行者123 更新时间:2023-12-03 21:16:46 25 4
gpt4 key购买 nike

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

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

现在,当我关闭弹出窗口时,服务器立即触发 terminate/2功能与 reason = {:shutdown, :closed} .扩展端不涉及关闭回调,所以这很棒!

但是当客户端只是失去了网络连接(我连接了第二台计算机并拔掉了网络插头)然后terminate/2不会触发。

为什么以及如何解决这个问题?

我玩弄了timeout transport :websocket, Phoenix.Transports.WebSocket 的选项但这没有成功。

更新:
随着新的令人敬畏的 Phoenix 1.2 Presence东西,这应该不再需要了。

最佳答案

正确的方法是不要在你的 channel 中捕获导出,而是让另一个进程监视你。当您下降时,它可以调用回调。以下是帮助您入门的片段:

# lib/my_app.ex

children = [
...
worker(ChannelWatcher, [:rooms])
]

# web/channels/room_channel.ex

def join("rooms:", <> id, params, socket) do
uid = socket.assigns.user_id]
:ok = ChannelWatcher.monitor(:rooms, self(), {__MODULE__, :leave, [id, uid]})

{:ok, socket}
end

def leave(room_id, user_id) do
# handle user leaving
end

# lib/my_app/channel_watcher.ex

defmodule ChannelWatcher do
use GenServer

## Client API

def monitor(server_name, pid, mfa) do
GenServer.call(server_name, {:monitor, pid, mfa})
end

def demonitor(server_name, pid) do
GenServer.call(server_name, {:demonitor, pid})
end

## Server API

def start_link(name) do
GenServer.start_link(__MODULE__, [], name: name)
end

def init(_) do
Process.flag(:trap_exit, true)
{:ok, %{channels: HashDict.new()}}
end

def handle_call({:monitor, pid, mfa}, _from, state) do
Process.link(pid)
{:reply, :ok, put_channel(state, pid, mfa)}
end

def handle_call({:demonitor, pid}, _from, state) do
case HashDict.fetch(state.channels, pid) do
:error -> {:reply, :ok, state}
{:ok, _mfa} ->
Process.unlink(pid)
{:reply, :ok, drop_channel(state, pid)}
end
end

def handle_info({:EXIT, pid, _reason}, state) do
case HashDict.fetch(state.channels, pid) do
:error -> {:noreply, state}
{:ok, {mod, func, args}} ->
Task.start_link(fn -> apply(mod, func, args) end)
{:noreply, drop_channel(state, pid)}
end
end

defp drop_channel(state, pid) do
%{state | channels: HashDict.delete(state.channels, pid)}
end

defp put_channel(state, pid, mfa) do
%{state | channels: HashDict.put(state.channels, pid, mfa)}
end
end
在 Elixir/Phoenix HashDict 的较新版本中已将名称更改为 Map。
较新代码库的正确示例是:
# lib/my_app.ex

children = [
...
worker(ChannelWatcher, [:rooms])
]

# web/channels/room_channel.ex

def join("rooms:", <> id, params, socket) do
uid = socket.assigns.user_id]
:ok = ChannelWatcher.monitor(:rooms, self(), {__MODULE__, :leave, [id, uid]})

{:ok, socket}
end

def leave(room_id, user_id) do
# handle user leaving
end

# lib/my_app/channel_watcher.ex

defmodule ChannelWatcher do
use GenServer

## Client API

def monitor(server_name, pid, mfa) do
GenServer.call(server_name, {:monitor, pid, mfa})
end

def demonitor(server_name, pid) do
GenServer.call(server_name, {:demonitor, pid})
end

## Server API

def start_link(name) do
GenServer.start_link(__MODULE__, [], name: name)
end

def init(_) do
Process.flag(:trap_exit, true)
{:ok, %{channels: Map.new()}}
end

def handle_call({:monitor, pid, mfa}, _from, state) do
Process.link(pid)
{:reply, :ok, put_channel(state, pid, mfa)}
end

def handle_call({:demonitor, pid}, _from, state) do
case Map.fetch(state.channels, pid) do
:error -> {:reply, :ok, state}
{:ok, _mfa} ->
Process.unlink(pid)
{:reply, :ok, drop_channel(state, pid)}
end
end

def handle_info({:EXIT, pid, _reason}, state) do
case Map.fetch(state.channels, pid) do
:error -> {:noreply, state}
{:ok, {mod, func, args}} ->
Task.start_link(fn -> apply(mod, func, args) end)
{:noreply, drop_channel(state, pid)}
end
end

defp drop_channel(state, pid) do
%{state | channels: Map.delete(state.channels, pid)}
end

defp put_channel(state, pid, mfa) do
%{state | channels: Map.put(state.channels, pid, mfa)}
end
end

关于websocket - 如何检测用户是否因网络断开而离开 Phoenix channel ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33934029/

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