3

I've created a server with no client code. I just have to type on the terminal telnet 127.0.0.1 4001 and on an other terminal telnet 127.0.0.2 4001 so when I type a message on the first terminal it appears on the same terminal, I know this an echo server, all I want is to modify this code if it's possible so the other client on other terminal could receive the message. This is an echo server code:

defmodule Multichat.Server do
  require Logger

  def accept(port) do
    {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: false, reuseaddr: true])
    Logger.info "Accepting connections on port #{port}"
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    {:ok, pid} = Task.Supervisor.start_child(Multichat.Server.TaskSupervisor, fn -> serve(client) end)
    :ok = :gen_tcp.controlling_process(client, pid)
    loop_acceptor(socket)
  end

  defp serve(socket) do
    socket
    |> read_line()
    |> write_line(socket)

    serve(socket)
  end

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

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

What should I change so when I open many clients using telnet they recieve messages from one another

Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
Mariem
  • 133
  • 5

1 Answers1

2

I think the simplest way to achieve this is to setup your socket in active mode and handle the messages for a single client in a GenServer. Then, by maintaining a list of all the client handlers, you can iterate over that list and send a message to each of the clients. A working, but not very clean version of this:

defmodule Multichat.ClientConnection do
  use GenServer

  def start_link(socket), do: GenServer.start_link(__MODULE__, socket)

  def handle_call({:send, message}, _from, socket) do
    :gen_tcp.send(socket, message)
    {:reply, :ok, socket}
  end

  def handle_info({:tcp, _socket, message}, socket) do
    for {_, pid, _, _} <- DynamicSupervisor.which_children(Multichat.Server.ConnectionSupervisor) do
      if pid != self() do
        GenServer.call(pid, {:send, message})
      end
    end

    {:noreply, socket}
  end
end

defmodule Multichat.Server do

  # ...

  def accept(port) do
    {:ok, socket} = :gen_tcp.listen(port, [:binary, packet: :line, active: true, reuseaddr: true])
    Logger.info "Accepting connections on port #{port}"
    loop_acceptor(socket)
  end

  defp loop_acceptor(socket) do
    {:ok, client} = :gen_tcp.accept(socket)
    {:ok, pid} = DynamicSupervisor.start_child(Multichat.Server.ConnectionSupervisor, {Multichat.ClientConnection, client})
    :ok = :gen_tcp.controlling_process(client, pid)
    loop_acceptor(socket)
  end

  # ...

end

Here, I'm using DynamicSupervisor.which_children/1 to get a list of the started client handlers. It might be a better idea to use a Registry for that purpose.

Paweł Obrok
  • 22,568
  • 8
  • 74
  • 70
  • Ok thank you. An other question what about the module Multichat.Server.Connection ? – Mariem Sep 09 '20 at 13:03
  • 1
    I don't understand your question - there is no `Multichat.Server.Connection` module in either my or your code. If you mean `Multichat.Server.ConnectionSupervisor`, then that's just the name of the supervisor, like `Multichat.Server.TaskSupervisor` in your code, no need for a module for it. – Paweł Obrok Sep 09 '20 at 14:12
  • Please, I have an other question. How is it possible to when I start the server on a port , on an other terminal I can not open other server with the same port. And when I open client on any port they keep receiving messages from each other . – Mariem Sep 10 '20 at 13:19
  • 1
    It looks like you should really be asking another question - SO isn't a chat or a help forum. However, briefly, it's not possible to have two servers with the same port open, because a port is like an identifier of the server. – Paweł Obrok Sep 10 '20 at 13:34
  • When I launch two nodes they connect on the same port, by the way I'm making an application called multichat you can find it [here](https://github.com/MARIEM96/Multichat) , so I want them to not be able to connect on that same port because for me they do . – Mariem Sep 10 '20 at 13:51