Trying to send udp messages from GenServer

Hi. I’ve got a genserver application that is working well, but a new requirement is that upon a call (or cast) request, I need to send a udp packet to another service. I can’t get this to work, and I fear that I’m missing knowledge of some part of the GenServer architecture. I’ve searched the web and this forum, but haven’t found anything that indicates it shouldn’t work.

Starting with what works:

get the socket from the genserver and send the packet:

{:ok, socket} = GenServer.call(ScEm, :get_socket)
:gen_udp.send(socket, ip, port, packet)

At this point, the server receives the message correctly.

But, in the context of the genserver itself, the following code never sends the packet:

defmodule ScEm do
use GenServer

@impl true
def handle_call({:send, packet}, _from, %State{socket: socket, ip: ip, port: port} = state) do
Logger.info(“sending = #{packet} to ip #{format_ip(ip)} port #{port}”)
response = :gen_udp.send(socket, ip, port, packet)
{:reply, response, state}
end

end

When I invoke the server with:
GenServer.call(ScEm, {:send, packet})

the external service doesn’t get anything. In the genserver’s log, I see that the ip and port are correct, and I know the socket is correct since in my working repl example, it is using the same socket.

you are missing a whole ton of code between the use GenServer and handle_call. There’s a lot of set up that is important. Are you opening the :gen_udp socket in start_link? init? (it should be init). Also it’s super strange that you have a call to get the socket. The socket should live inside the GenServer as private encapsulated state and you should probably never be sendit it out of the GenServer.

If you want an example of how to set up a UDP-based GenServer, have a look here: https://github.com/ityonemo/trivial/blob/master/lib/trivial/server.ex

Note this is an active: true UDP connection. So packets from the counterparty will arrive as messages to handle_info; if you want an active: false connection you have to poll the socket with recv.

3 Likes

Thanks for the link. That was very helpful. I found the bug - it was mine and is embarrassing (now that I’ve found it). In the code I gave, I was only highlighting the part of the code that wasn’t working. There was an ellipsis to indicate the missing code. However, with your example I made some useful modifications, so thanks again.

As for the socket, I agree. I’d only put it into the server so I could test externally to ensure I was using the same socket. It is removed now that it’s working.

And thanks for explaining the active: false vs. true. I was confused about that point.

3 Likes

yep! Since writing the TFTP stuff I’ve switched over to preferring active: false, because it reduces the likelihood of an overflowing mailbox (but TBH I never had any problems with running the TFTP server in active: true).