Hi everyone,
I’m implementing something that needs to read a TCP stream for the first time and trying to get my head around gen_tcp
. I start a GenServer
to handle the connection and want to be able to make other calls to it so I thought spawn
ing the blocking :gen_tcp.accept
call would be efficient but it seems the socket doesn’t survive being transferred between processes. Does anyone know why? Or if I’m making an elementary mistake here?
Here’s a minimal script that demonstrates the problem:
# testtcp.exs
{:ok, socket} = :gen_tcp.listen(0, [])
{:ok, port} = :inet.port(socket) |> dbg()
current = self()
# Spawn a process just for accepting connections and send the new connection back to use when it
# happens.
spawn(fn ->
case :gen_tcp.accept(socket) do
{:ok, conn} ->
dbg(:inet.sockname(conn))
send(current, {:new_conn, conn} |> dbg())
end
end)
# Spawn to connect to the socket.
spawn(fn ->
dbg(:gen_tcp.connect('localhost', port, [], :infinity))
end)
receive do
{:new_conn, conn} ->
dbg({:received_conn, conn})
dbg(:inet.sockname(conn))
end
Which outputs:
[tcptest.exs:3: (file)]
:inet.port(socket) #=> {:ok, 40015}
[tcptest.exs:8: (file)]
:inet.sockname(conn) #=> {:ok, {{127, 0, 0, 1}, 40015}}
[tcptest.exs:14: (file)]
:gen_tcp.connect('localhost', port, [], :infinity) #=> {:ok, #Port<0.7>}
[tcptest.exs:9: (file)]
{:new_conn, conn} #=> {:new_conn, #Port<0.8>}
[tcptest.exs:19: (file)]
{:received_conn, conn} #=> {:received_conn, #Port<0.8>}
[tcptest.exs:20: (file)]
:inet.sockname(conn) #=> {:error, :einval}
(there’s no way to add line numbers to code blocks here right?)
You’ll notice the dbg
output is slightly out of order, but it shows the change between calling :inet.sockname
in the same process that accepted the socket (valid) and in the receive
loop ({:error, :einval}
).
Thanks for any help!
Andrew