Myles McDonnell
Myles McDonnell

Reputation: 13335

Erlang OTP UDP Server

Here is a simple UDP server:

-module(kvstore_udpserver).
-author("mylesmcdonnell").

%% API
-export([start/0]).

start() ->
  spawn(fun() -> server(2346) end).

server(Port) ->
  {ok, Socket} = gen_udp:open(Port, [binary]),
  loop(Socket).

loop(Socket) ->
  receive
    {udp, Socket, Host, Port, Bin} ->
      case binary_to_term(Bin) of
        {store, Value} ->
          io:format("kvstore_udpserver:{store, Value}~n"),
          gen_udp:send(Socket,Host,Port,term_to_binary(kvstore:store(Value)));
        {retrieve, Key} ->
          io:format("kvstore_udpserver:{retrieve, Value}~n"),
          gen_udp:send(Socket,Host,Port,term_to_binary(kvstore:retrieve(Key)))
      end,
      loop(Socket)
  end.

How can I restructure this so that

a) It, or at least the relevant part of it, is a gen_server so that I can add to the supervision tree

b) increase concurrency by handling each message in a separate process.

I have reimplemented the sockserv example from Learn You Some Erlang for my TCP server but I'm struggling to determine a similar model for UDP.

Upvotes: 5

Views: 1951

Answers (1)

dethtron5000
dethtron5000

Reputation: 10841

For a):

  1. You need to delcare the gen_server behaviour and implement all the callback functions (this is obvious, but it's worth calling out explicitly). If you have rebar installed, you can use the command rebar create template=simplesrv srvid=your_server_name to add the boilerplate functions.

  2. You'd probably want to move the server starting business logic (the gen_udp:open/2 call) to your server's init/1 function. (The init is required by the gen_server behaviour. You can also start your loop/1 function there.

  3. You'd probably want to make sure the udp server is closed by the module's terminate/2 function.

  4. Move the business logic for handling requests that come in from parsing the messages to your loop/1 function into handle_call/3 or handle_cast/2 in your module (see below).

For b): You have a few options, but basically, whenever you receive a message, you can use gen_server:cast/2 (if you don't care about the response) or gen_server:call/2,3 if you do. The casts or calls will be handled by the handle_cast/2 or handle_call/3 functions in your module.

Casts are inherently non-blocking and the answers to this question have a good design pattern for handling call operations asynchronously in gen_servers. You can crib from that.

Upvotes: 4

Related Questions