├── .gitignore ├── LICENSE ├── lib ├── simple_tcp.ex ├── simple_worker.ex └── simpletcp_sender.ex ├── mix.exs └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /cover 3 | /deps 4 | erl_crash.dump 5 | *.ez 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Kyle Hanson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/simple_tcp.ex: -------------------------------------------------------------------------------- 1 | defmodule SimpleTCP do 2 | use Application 3 | 4 | def start(_type, _args) do 5 | import Supervisor.Spec, warn: false 6 | 7 | children = [ 8 | worker(SimpleTCP.Worker, [8000]) 9 | ] 10 | 11 | opts = [strategy: :one_for_one, name: SimpleTCP.Supervisor] 12 | Supervisor.start_link(children, opts) 13 | 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /lib/simple_worker.ex: -------------------------------------------------------------------------------- 1 | defmodule SimpleTCP.Worker do 2 | 3 | import Socket 4 | 5 | def start_link(port) do 6 | pid = spawn_link(fn -> init(port) end) 7 | {:ok, pid} 8 | end 9 | 10 | def init(port) do 11 | server = Socket.TCP.listen!(port, [packet: :line]) 12 | loop_connection(server) 13 | end 14 | 15 | defp loop_connection(server) do 16 | 17 | # Accept a TCP connection 18 | client = Socket.TCP.accept!(server) 19 | 20 | # Start our listening process in another process so it doesn't block 21 | spawn(fn() -> init_listener(client) end) 22 | 23 | # Get the next connection 24 | loop_connection(server) 25 | end 26 | 27 | def init_listener(client) do 28 | 29 | # Start our sending process. 30 | {:ok, _pid} = SimpleTCP.Sender.start_link(client) 31 | listen_for_msg(client) 32 | 33 | end 34 | 35 | defp listen_for_msg(client) do 36 | 37 | case Socket.Stream.recv(client) do 38 | { :ok, data } -> 39 | # Use gproc to cast the message {:msg, data} to everyone subscribed to :something 40 | GenServer.cast({:via, :gproc, {:p, :l, :something}}, {:msg, data}) 41 | # Loop for another message 42 | listen_for_msg(client) 43 | { :error, :closed } -> :ok 44 | other -> IO.inspect other 45 | end 46 | 47 | end 48 | 49 | end 50 | -------------------------------------------------------------------------------- /lib/simpletcp_sender.ex: -------------------------------------------------------------------------------- 1 | defmodule SimpleTCP.Sender do 2 | use GenServer 3 | import Socket 4 | 5 | def start_link(socket, opts \\ []) do 6 | GenServer.start_link(__MODULE__, [socket: socket], opts) 7 | end 8 | 9 | def init(socket) do 10 | # Register the process with gproc and subcscribe to :something 11 | :gproc.reg({:p, :l, :something}) 12 | {:ok, socket} 13 | end 14 | 15 | def handle_cast({:msg, msg}, [socket: socket] = state) do 16 | Socket.Stream.send(socket, msg) 17 | {:noreply, state} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule SimpleTCP.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [app: :simpleserver, 6 | version: "0.0.1", 7 | elixir: "~> 1.2", 8 | build_embedded: Mix.env == :prod, 9 | start_permanent: Mix.env == :prod, 10 | deps: deps] 11 | end 12 | 13 | # Configuration for the OTP application 14 | # 15 | # Type "mix help compile.app" for more information 16 | def application do 17 | [applications: [:logger, :connection, :gproc, :socket], 18 | mod: {SimpleTCP, []}] 19 | end 20 | 21 | # Dependencies can be Hex packages: 22 | # 23 | # {:mydep, "~> 0.3.0"} 24 | # 25 | # Or git/path repositories: 26 | # 27 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 28 | # 29 | # Type "mix help deps" for more examples and options 30 | defp deps do 31 | [{:connection, "1.0.2" }, 32 | {:gproc, "~> 0.5"}, 33 | {:socket, "~> 0.3"}] 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Elixir TCP and Gproc 2 | A simple server demonstrating sockets and gproc broadcasting in Elixir. 3 | 4 | Run the server using `iex -S mix` and connect multiple clients via telnet. Messages should be broadcast to all clients. 5 | --------------------------------------------------------------------------------