lib/tcptest/echo/server.ex
defmodule Tcptest.Server do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, :no_args, name: __MODULE__)
end
def init(:no_args) do
{:ok, socket} =
:gen_tcp.listen(2566, [:binary, packet: :line, active: false, reuseaddr: true])
IO.puts("Server is listening on port 2566...")
Process.send_after(self(), {:loop, socket}, 0)
{:ok, nil}
end
def handle_info({:loop, socket}, _) do
accept(socket)
end
defp accept(socket) do
{:ok, client_socket} = :gen_tcp.accept(socket)
spawn_link(Tcptest.Handler, :start_link, [client_socket])
send(self(), {:loop, socket})
{:noreply, nil}
end
end
2566번 포트를 열고 라인으로 구분되는 패킷을 받는다.
handle_info
콜백이 :loop 메시지를 받으면,Tcptest.Handler
프로세스를 생성하고, 또 다음 연결을 수신할 수 있도록, 자기 자신에게 :loop 메시지를 다시 보낸다 (메시지 이름이 loop인 이유!)
lib/tcptest/echo/handler.ex
defmodule Tcptest.Handler do
use GenServer
def start_link(socket) do
GenServer.start_link(__MODULE__, socket)
end
def init(client_socket) do
:gen_tcp.send(client_socket, "WELCOME\r\n")
Process.send_after(self(), {:tcp, client_socket}, 0)
{:ok, nil}
end
def handle_info({:tcp, socket}, _) do
case :gen_tcp.recv(socket, 0, 1000 * 30) do
{:ok, data} ->
data = String.trim(data)
handle_msg(socket, data)
{:error, reason} ->
IO.puts("err on recv from socket > #{inspect(reason)}")
handle_msg(socket, "CLOSE")
end
{:noreply, nil}
end
def handle_msg(socket, "CLOSE") do
:gen_tcp.close(socket)
{:stop, :normal, nil}
end
def handle_msg(socket, data) do
:gen_tcp.send(socket, "Echo: #{data}\n")
send(self(), {:tcp, socket})
{:noreply, nil}
end
end
Tcptest.Server
로 부터Tcptest.Handler
프로세스가 생성된다.Tcptest.Handler
는 tcp 연결 요청을 보낸 클라이언트에게 WELCOME 메시지를 보내고, 응답을 수신한다. 이때 타임아웃은 30초로 설정해두엇다.정상적인 메시지가 수신되면
handle_msg
함수를 호출해서 Echoing 한다. 만약 메시지가CLOSE
라면 Echoing 하지 않고 종료한다.비정상적인 메시지가 수신되면 로그를 남기고 종료한다.