How to change owner of TCP socket

Hi, I’m having trouble with changing the owner of the TCP socket. I tried inet:tcp_controlling_process/2 and gen_tcp:controlling_process/2 and they return ‘ok’, but when the previous controlling proces dies, i receive ‘tcp_closed, Port’ on the new controlling process. What am I doing wrong?

2 Likes

I can’t reproduce that, so please elaborate :sweat_smile:

I run nc -l 8888 in one shell.

In an Erlang shell, I run…

1> Self=self(), spawn(fun () -> {ok, S}=gen_tcp:connect({127, 0, 0, 1}, 8888, [{active, true}]), gen_tcp:controlling_process(S, Self) end).
<0.123.0>

Doing a flush() just returns ok at that point. Only when I exit nc, I get…

3> flush().
Shell got {tcp_closed,#Port<0.9>}

… as expected.

3 Likes

The documentation says:

The controlling process is the process that receives messages from the socket.

You could test if the owning process has been successfully changed by checking if it is the correct process that receives the messages.

If the owner is successfully changed, but the socket still dies when the previous owner dies, I suspect this is because the processes are linked, which will cause them to die together.

From documentation on links

If one of the participants of a link terminates, it will send an exit signal to the other participant. The exit signal will contain the exit reason of the terminated participant.

Should this be the case the link can be removed using the unlink/1 function.

If you want to know more about, links monitors and errors in Erlang, I suggest chapter 13 of Joe Armstrongs programming erlang second edition

3 Likes

If it receives the tcp_closed message, it is the correct process. tcp_closed (together with tcp and tcp_error) is one of those messages the controlling process (and no other) receives. So unless the OP is actually mixing up processes here, that can’t be the problem.

And linking TCP ports… I don’t know anyone or anything that does this, outside of the internals of the tcp/inet modules at least. So I at least doubt that this is the problem, either :sweat_smile:

I guess we’ll have to wait for further details :wink:

@mmin, could it be that the old owning process actually calls gen_tcp:close on the socket, maybe as part of its shutdown routine, maybe if it is a gen_server or similar?

4 Likes
get_sockets(ServerPort) ->
    get_sockets(ServerPort, [binary], []).

get_sockets(ServerPort, ListenOpts, ConnectOpts) ->
    spawn(fun () -> server(ServerPort, ListenOpts, self()) end),
    {ok, ClientSocket} = gen_tcp:connect("localhost", ServerPort, ConnectOpts, ?DEF_TOUT),
    receive
        {ok, ServerSocket} -> {ServerSocket, ClientSocket};
        What -> throw({error_tcp_conn,What})
    after ?DEF_TOUT ->
        throw({timeout, serversocket})
    end.
server(ServerPort, ListenOpts, Parent) ->
    {ok, ListenSocket} = gen_tcp:listen(ServerPort,ListenOpts),
    {ok, ServSock} = gen_tcp:accept(ListenSocket,?DEF_TOUT),
    ok = gen_tcp:controlling_process(ServSock, Parent),
    Parent ! {ok, ServSock}.

So this is the case. For testing purposes, I’m trying to get both server and client socket because I don’t want my tests to depend on httpd or something similar. No gen_servers or anything are used, this is all of the code that is causing the issue. Maybe I’m missing some options for listening or connecting? DEF_TOUT is set to 5000.

edit: Lol, I found a mistake instantly after this reply… It was self() in lambda :upside_down_face: … Btw thanks for replies :smiley:

3 Likes

I see, so the spawned process running server/3 is making itself the owning process, ie socket ownership does effectively not change, and when the spawned process ends it takes the socket down also :sweat_smile: So your problem is solved, right? (If yes, please mark this question thread as solved :wink:)

4 Likes