Skip to content

Instantly share code, notes, and snippets.

@t4sk
Last active December 19, 2023 18:17
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save t4sk/f84ae820b0f1a92a5c828baacbb95ce1 to your computer and use it in GitHub Desktop.
Save t4sk/f84ae820b0f1a92a5c828baacbb95ce1 to your computer and use it in GitHub Desktop.
gen_tcp and GenServer example in Elixir (1.5.2)

gen_tcp and GenServer example in Elixir (1.5.2)

Usage

server.ex

Server.start

Test the server

telnet 127.0.0.1 8000
> Hello World

client.ex

Server.start

In another terminal

{:ok, pid} = Client.start
Client.send_message(pid, "Hello World")
defmodule Client do
require Logger
use GenServer
@ip {127, 0, 0, 1}
@port 8000
def send_message(pid, message) do
GenServer.cast(pid, {:message, message})
end
def start do
GenServer.start(__MODULE__, %{socket: nil})
end
def init(state) do
send(self(), :connect)
{:ok, state}
end
def handle_info(:connect, state) do
Logger.info "Connecting to #{:inet.ntoa(@ip)}:#{@port}"
case :gen_tcp.connect(@ip, @port, [:binary, active: true]) do
{:ok, socket} ->
{:noreply, %{state | socket: socket}}
{:error, reason} ->
disconnect(state, reason)
end
end
def handle_info({:tcp, _, data}, state) do
Logger.info "Received #{data}"
{:noreply, state}
end
def handle_info({:tcp_closed, _}, state), do: {:stop, :normal, state}
def handle_info({:tcp_error, _}, state), do: {:stop, :normal, state}
def handle_cast({:message, message}, %{socket: socket} = state) do
Logger.info "Sending #{message}"
:ok = :gen_tcp.send(socket, message)
{:noreply, state}
end
def disconnect(state, reason) do
Logger.info "Disconnected: #{reason}"
{:stop, :normal, state}
end
end
defmodule Server do
require Logger
use GenServer
@port 8000
def start do
GenServer.start(__MODULE__, %{socket: nil})
end
def init(state) do
{:ok, socket} = :gen_tcp.listen(@port, [:binary, active: true])
send(self(), :accept)
Logger.info "Accepting connection on port #{@port}..."
{:ok, %{state | socket: socket}}
end
def handle_info(:accept, %{socket: socket} = state) do
{:ok, _} = :gen_tcp.accept(socket)
Logger.info "Client connected"
{:noreply, state}
end
def handle_info({:tcp, socket, data}, state) do
Logger.info "Received #{data}"
Logger.info "Sending it back"
:ok = :gen_tcp.send(socket, data)
{:noreply, state}
end
def handle_info({:tcp_closed, _}, state), do: {:stop, :normal, state}
def handle_info({:tcp_error, _}, state), do: {:stop, :normal, state}
end
@hryniuk
Copy link

hryniuk commented Sep 29, 2020

Thanks, that's really helpful!

@t4sk
Copy link
Author

t4sk commented Sep 30, 2020

@hryniuk
Your welcome. I am surprised this code is still useful

@hryniuk
Copy link

hryniuk commented Sep 30, 2020

I got the same situation with one of my gists and I thought it would be good to say "thank you" :)

I've just started with Elixir and I was trying to do the client part. I did a loop with :gen_tcp.recv and wanted to switch to "active" socket, but around 95% of guides I've found were only about writing tcp servers. This code is exactly what I needed, thank you again for sharing. I'd definitely need a while to figure this out on my own.

@t4sk
Copy link
Author

t4sk commented Oct 1, 2020

@hryniuk Thanks for saying thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment