我正在使用基于Elixir的网络扫描仪。 我的目标是能够在代理(简单)中存储IP地址列表并在此代理上循环工作人员,以便他们获取列表的头部并启动扫描/探测功能(不那么容易)。 虽然这可能听起来像某种理论上的东西,但如果有帮助,我可以提供代码片段。
编辑:显然我设法弄清楚如何这样做(我想这就是人们会如何做到的)。
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end
项目已移至GitHub 再次感谢o /
答案 0 :(得分:0)
一种简单的(hack-ish)方式来做你所描述的[我认为]:
defmodule MultiScanner do
def scan(range) do
main_proc = self
Enum.each(range, fn(ip) ->
spawn_link(fn ->
res = Find3r.Utils.scan(ip)
send main_proc, {:ip_results, ip, res}
end)
end)
ip_map = Enum.map(range, fn(ip) -> {ip, nil} end)
|> Enum.into(%{})
wait_for_results(ip_map)
end
defp wait_for_results(ip_map, acc \\ %{})
defp wait_for_results(ip_map, acc) when map_size(ip_map) == 0, do: acc
defp wait_for_results(ip_map, acc) do
receive do
{:ip_results, ip, res} ->
acc = Map.put(acc, ip, res)
ip_map = Map.delete(ip_map, ip)
wait_for_results(ip_map, acc)
end
end
end
这将为每个运行扫描的IP地址生成一个进程,并将结果发送回调用进程,然后调用进程将结果折叠为ip =>的映射。结果
这种方法显然存在许多问题 - 最明显的是,您可能不希望所有同时开始扫描。
有很多方法可以解决这个问题,但除了评论中建议的poolboy
之类的预制解决方案,或:gproc
之外 - 如果我要实际实施强大的解决方案对于这种方法,我可能会把这样的监督树放在一起:
Scan.Supervisor
-ScanWorker.Supervisor (simple-one-for-one)
-ScanWorker (anon GenServer, executing a single scan)
-ScanWorker
...
-ScanWorker.Manager (Named GenServer, coordinates the ScanWorkers)
我的信封架构背面将有上面的4个模块,以及一个用于保存公共API的顶级模块Scan
。我还想象它需要一个小时左右才能充实到我不会尴尬地向同事展示它。
我开始为你勾勒出这一点 - 但坦率地说,你的问题含糊不清,写得不好。你似乎没有花太多时间写它,甚至不愿意校对它。我在这里汇总的答案可能比你的问题更加慷慨:如果你花更多的时间和精力写下你的问题,我认为你会在SO上找到更好的成功。
答案 1 :(得分:0)
I managed to figure out how to do so (I guess that's how one would have done so).
defmodule Find3r.Worker do
use GenServer
require Logger
def start_link, do: GenServer.start_link(__MODULE__, :ok, name: :worker)
def init(:ok) do
Logger.debug("Worker activated.")
{:ok, :ok}
end
def scan_range(range), do: GenServer.call(__MODULE__, {:scan_range, range})
def handle_call({:scan_range, range}, _from, :ok) do
Logger.debug("Scanning #{range}")
{:ok, pid} = Agent.start(Find3r.Utils, :addresses_for, [range])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
{:reply, :ok, :ok}
end
# Avoids an exception when the Agent runs out of addresses
defp foo([ip|rest]), do: {ip, rest}
defp foo(_), do: {[], []}
defp loop(ip, pid) when is_tuple(ip) do
spawn(Find3r.Utils, :scan, [ip])
Agent.get_and_update(pid, fn(state) -> foo(state) end) |> loop(pid)
end
defp loop(_, _), do: nil
end