Hello everyone! Late last night for some reason I decided to look at how I was sending last messages and closing sockets. I wanted to make sure it’s correct so back to the documentation I went…
To give you a bit of context, in one of my modules, I start the socket with the following options:
binary, {active, false}, {nodelay, true}, {reuseaddr, true}, {debug, false}, {backlog, 24}
Then in each client I do:
inet:setopts(Socket, [{active, once}])
And dealing with sends like so:
_ = gen_tcp:send(Socket, Data)
In one of the requests, I pretty much need to send one last message to my client and close the connection. For that, I was doing:
_ = gen_tcp:send(Socket, Data),
{stop, normal, State}.
But that doesn’t seem to be correct.
Reading the documentation, I found shutdown and a few additional flags/options (send_timeout, send_timeout_close and linger which I ended up not using it because according to the AI is not a reliable flag, not sure if that’s true or not…)
I ended up with the following:
send_packet(PID, Packet) ->
gen_server:cast(PID, {send_packet, Packet}).
shutdown_socket(PID) ->
gen_server:cast(PID, {shutdown_socket}).
init([custom_game, GamePID, Socket]) ->
ok = inet:setopts(Socket, [{send_timeout, 5000}]),
State = #state{socket = Socket, game_pid = GamePID},
{ok, State};
handle_info(timeout, State) ->
Socket = State#state.socket,
gen_tcp:close(Socket),
{stop, normal, State};
handle_cast({send_packet, Packet}, State) ->
Socket = State#state.socket,
case gen_tcp:send(Socket, Packet) of
ok ->
{noreply, State};
{error, _Reason} ->
gen_tcp:close(Socket),
{stop, normal, State}
end;
handle_cast({shutdown_socket}, State) ->
Socket = State#state.socket,
case gen_tcp:shutdown(Socket, write) of
ok ->
{noreply, State, 5000};
{error, _Reason} ->
gen_tcp:close(Socket),
{stop, normal, State}
end;
With this, let’s say I need to send one last message before terminating the gen_server, I can do:
send_packet(self(), Data),
shutdown_socket(self()),
{noreply, State}
My understanding is that:
- By calling
shutdown(write)we signal the client that we want to close the connection. When the client reads, it will eventually read0meaning, we need to close the connection. - In the ideal scenario, the client will call
closeon its end (my client uses Python) and this will result inhandle_info({tcp_closed, _Socket}, State)being called - If the client hangs or simply disconnects, the client wont call
closeand that’s why I need to have a timer to eventuallycloseif nothing happens after a few seconds
Would this be correct?
Thanks!