Reputation: 13335
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
Reputation: 10841
For a):
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.
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.
You'd probably want to make sure the udp server is closed by the module's terminate/2
function.
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