View Source Protocol

In this section, we will learn how to use ElvenGard.Network.Endpoint.Protocol.

An Protocol is wrapper around Ranch protocols.

configuration

Configuration

Configuration is performed via the config/config.exs file as you did previously.

config :login_server, LoginServer.Endpoint.Protocol,
  packet_handler: LoginServer.Endpoint.PacketHandler,
  network_codec: LoginServer.Endpoint.NetworkCodec

Here's the explanation for these options:

  • packet_handler: LoginServer.PacketHandler: specifies the module responsible for handling packets received from clients.
  • network_codec: LoginServer.NetworkCodec: Specifies the module responsible for encoding and decoding packets for communication between clients and the server.

create-a-protocol

Create a Protocol

For this part, we're going to create a fairly simple protocol that will just display our packets.

# file: lib/login_server/endpoint/protocol.ex
defmodule LoginServer.Endpoint.Protocol do
  @moduledoc """
  Documentation for LoginServer.Endpoint.Protocol
  """

  use ElvenGard.Network.Endpoint.Protocol

  require Logger

  alias ElvenGard.Network.Socket

  ## Callbacks

  @impl true
  def handle_init(%Socket{} = socket) do
    Logger.info("New connection: #{socket.id}")

    %Socket{transport: transport, transport_pid: transport_pid} = socket
    :ok = transport.setopts(transport_pid, packet: :line, reuseaddr: true)

    {:ok, socket}
  end

  @impl true
  def handle_message(message, %Socket{} = socket) do
    Logger.debug("New message from #{socket.id}: #{inspect(message)}")
    :ignore
  end

  @impl true
  def handle_halt(reason, %Socket{} = socket) do
    Logger.info("#{socket.id} is now disconnected (reason: #{inspect(reason)})")
    {:ok, socket}
  end
end

Once again, creating a Protocol is fairly straightforward.

This example defines 3 callbacks :

  • handle_init/1: called when a client connects, it is mainly used to set socket options or call ElvenGard.Network.Socket.assign/2 to init assigns.
  • handle_message/2: called when we receive a packet from a client, we can either ignore it by returning :ignore, or choose to decode it and then handle it by returning :ok.
  • handle_halt/2: called when a client disconnects.

NOTE: you may notice that we define the packet: :line option in handle_init/1. We use this option because we want to use a line break as a separator for our packets. This works because, according to our network protocol, we use a text protocol where each packet is separated by a \n. However, for a binary protocol, you may need to use packet: :raw or other options.
For more information on available options, see :inet.setopts/2.

summary

Summary

If you run your application in its current state, you'll see that it's possible to connect to our server and send it messages, and that these are displayed by our application.
Since we're using a text-based protocol, you can use Netcat as client for example.

Now that packets can be received, they need to be decoded and processed. This is what we'll see next.