Elixir 构建 TCP 应用

defmodule EliApp do
  use Application

  def start(_type, _args) do
    TCPServer.Supervisor.start_link([])
  end
end

defmodule TCPServer do
  use GenServer

  def start_link(config \\ [{0,0,0,0}, 8010]) do
    GenServer.start_link(__MODULE__, config, name: __MODULE__)
  end

  def init([ip, port]) do
    {:ok, socket}= :gen_tcp.listen(port,[:binary, packet: :line, active: false, reuseaddr: true, ip: ip])
    IO.puts("TCP listened at #{port} ...")
    loop_accept(socket)
  end

  def loop_accept(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    # 每一个连接单独开启一个进程,当多个客户端连接的时候,每个客户端对应不同的独立进程,不会相互影响和阻塞,
    # 每个独立进程都由Supervisor来管理,当一个进程崩溃的时候,不会影响到主进程,增加可用性和容错性
    {:ok, pid} = Task.Supervisor.start_child(TCPServer.TaskSupervisor, fn ->server(client) end)
    :ok = :gen_tcp.controlling_process(client, pid)
    loop_accept(socket)
  end

  def server(socket) do
    socket
    |> read_line()
    |> String.split(" ")
    |> Enum.filter(& &1 != "")
    |> handler()
    |> write_line(socket)

    server(socket)
  end

  defp read_line(socket) do
    {:ok, data} = :gen_tcp.recv(socket, 0)
    data
  end

  defp handler(line) do
    case line do
      ["PUT", msg] -> "put " <> msg
      ["GET", msg] -> "get " <> msg
      ["ERROR", msg] -> raise "error: " <> msg
      [_] -> "what ever\r\n"
    end
  end

  defp write_line(line, socket) do
    :gen_tcp.send(socket, line)
  end

end

defmodule TCPServer.Task do
  use Task, restart: :permanent

  def start_link(opts) do
    Task.start_link(__MODULE__, :run, [opts])
  end

  def run(opts) do
    TCPServer.start_link(opts)
  end
end

defmodule TCPServer.Supervisor do
  use Supervisor

  def start_link(opts) do
    Supervisor.start_link(__MODULE__, :ok, opts)
  end

  def init(:ok) do
    children = [
      {Task.Supervisor, name: TCPServer.TaskSupervisor},
      {TCPServer.Task, [{0, 0, 0, 0}, 8020]}
    ]
    Supervisor.init(children, strategy: :one_for_one)
  end

end