如何在Elixir中的工作进程之间共享数据?

时间:2017-08-15 08:58:43

标签: networking erlang udp elixir

我有2名工人

worker(Mmoserver.MessageReceiver, []),
worker(Mmoserver.Main, [])

MessageReceiver将等待直到在TCP上接收到消息并处理它们,主循环将获取该信息并对其进行操作。如何与worker2共享worker1获取的信息?

Mmoserver.ex
这是启动工作人员的主文件

defmodule Mmoserver do
  use Application

  def start(_type, _args) do
    import Supervisor.Spec, warn: false

    IO.puts "Listening for packets..."

    children = [
      # We will add our children here later

      worker(Mmoserver.MessageReceiver, []),
      worker(Mmoserver.Main, [])
    ]

    # Start the main supervisor, and restart failed children individually
    opts = [strategy: :one_for_one, name: AcmeUdpLogger.Supervisor]
    Supervisor.start_link(children, opts)
  end

end

MessageReceiver.ex
这只会启动一个tcp监听器。它应该能够获取消息,找出它是什么(通过它的id)然后解析数据并将其发送到Main中的特定函数

defmodule Mmoserver.MessageReceiver do
  use GenServer
  require Logger

  def start_link(opts \\ []) do
    GenServer.start_link(__MODULE__, :ok, opts)
  end

  def init (:ok) do
    {:ok, _socket} = :gen_udp.open(21337)
  end

  # Handle UDP data
  def handle_info({:udp, _socket, _ip, _port, data}, state) do
    parse_packet(data)
    # Logger.info "Received a secret message! " <> inspect(message)
    {:noreply, state}
  end

  # Ignore everything else
  def handle_info({_, _socket}, state) do
    {:noreply, state}
  end

  def parse_packet(data) do
    # Convert data to string, then split all data
    # WARNING - SPLIT MAY BE EXPENSIVE
    dataString = Kernel.inspect(data)
    vars = String.split(dataString, ",")

    # Get variables
    packetID = Enum.at(vars, 0)
    x = Enum.at(vars, 1)

    # Do stuff with them
    IO.puts "Packet ID:"
    IO.puts packetID
    IO.puts x

    # send data to main
    Mmoserver.Main.handle_data(vars)
  end
end

Main.ex
这是主循环。它将处理tcp侦听器接收的所有最新数据并对其进行操作。最终它也将更新游戏状态。

defmodule Mmoserver.Main do
  use GenServer

  @tickDelay 33

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

  def init (state) do

    IO.puts "Main Server Loop started..."

    # start the main loop, parameter is the initial tick value
    mainLoop(0)

    # return, why 1??
    {:ok, 1}
  end

  def handle_data(data) do
    GenServer.cast(:main, {:handle_data, data})
  end

  def handle_info({:handle_data, data}, state) do
    # my_function(data)
    IO.puts "Got here2"
    IO.puts inspect(data)
    {:noreply, state}
  end

  # calls respective game functions
  def mainLoop(-1) do
    IO.inspect "Server Loop has ended!" # base case, end of loop
  end

  def mainLoop(times) do
    # do shit
    # IO.inspect(times) # operation, or body of for loop

    # sleep
    :timer.sleep(@tickDelay);

    # continue the loop RECURSIVELY
    mainLoop(times + 1)
  end

end

1 个答案:

答案 0 :(得分:1)

由于Mmoserver.MessageReceiver将向Mmoserver.Main发送消息, Main 必须首先启动,此外,还需要关联名称:

worker(Mmoserver.Main, []),
worker(Mmoserver.MessageReceiver, [])

最简单的方法可能是Mmoserver.Main,假设它是GenServer

defmodule Mmoserver.Main do
  use GenServer

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

  # ...
end

您可以添加便利功能,以及类似的实现:

defmodule Mmoserver.Main do
  # ...

  def handle_data(data) do
    GenServer.cast(:main, {:handle_data, data})
  end

  def handle_info({:handle_data, data}, state) do
    my_function(data)
    {:noreply, state}
  end
end

因此,您的MessageReceiver可以发送如下消息:

defmodule Mmoserver.MessageReceiver do
  def when_data_received(data) do
    Mmoserver.Main.handle_data(data)
  end
end

这假设Mmoserver.MessageReceiver并不期望Mmoserver.Main做出回应。我已经决定这样做,因为你没有指定你想要处理数据的方式,这似乎是如何做到这一点的简单例子。