Reputation: 424
Hii the problem is that i created a tcp server socket in erlang, then after accept the socket i want to handle that on another node, but it gives {error, closed}.
a small demo :-
-module(tcp_server).
-compile(export_all). %% just for testing
start() ->
{ok, ListeningSocket} = gen_tcp:listen(4444, [{active, false}, binary]),
wait_for_next(ListeningSocket).
wait_for_next(ListeningSocket) ->
%% waiting for requests
{ok, Socket} = gen_tcp:accept(ListeningSocket),
_ = spawn('[email protected]', ?MODULE, handle_request, [Socket]),
wait_for_next(ListeningSocket).
handle_request(Socket) ->
{ok, Data} = gen_tcp:recv(Socket, 0), %% this always return {error, closed} ??
io:format("~p~n", [Data]),
gen_tcp:close(Socket).
start server by :
erl -name [email protected]
c(tcp_server). %% compile and load
tcp_server:start(). %% started
remote node by :
erl -name [email protected]
c(tcp_server). %% so here handle_request has been loaded
the remote node can also be on another machine ? what i m doing wrong ? or is that possible, sorry for weak english and thanks in advance ! ;)
Upvotes: 1
Views: 400
Reputation: 386
A very interesting example! Sockets are bound to the OS process (Beam VM) that is listening to the given port.
While the spirit of Distributed Erlang would suggest that a Socket is just like any other process and can be used transparently across Erlang nodes, this turns out not to be the case.
I added some extra debug info to your code to show that the socket object passed to a different node is not accessible in any meaningful way:
wait_for_next(ListeningSocket) ->
{ok, Socket} = gen_tcp:accept(ListeningSocket),
error_logger:info_msg("Socket info: ~p~n", [inet:sockname(Socket)]),
gen_tcp:controlling_process(
Socket,
spawn('[email protected]', ?MODULE, handle_request, [Socket])),
wait_for_next(ListeningSocket).
handle_request(Socket) ->
error_logger:info_msg("Socket info: ~p~n", [inet:sockname(Socket)]),
{ok, Data} = gen_tcp:recv(Socket, 0),
io:format("~p~n", [Data]),
gen_tcp:close(Socket).
Running this live gives us:
$ erlc ./tcp_server.erl && erl -name [email protected] -s tcp_server start
Eshell V5.9.3 (abort with ^G)
([email protected])1>
=INFO REPORT==== 29-Nov-2015::18:17:08 ===
Socket info: {ok,{{127,0,0,1},4444}}
([email protected])1>
=INFO REPORT==== 29-Nov-2015::18:17:08 ===
Socket info: {error,einval}
** at node [email protected] **
([email protected])1>
=ERROR REPORT==== 29-Nov-2015::18:17:08 ===
Error in process <0.43.0> on node '[email protected]' with exit value: {{badmatch,{error,closed}},[{tcp_server,handle_request,1,[{file,"tcp_server.erl"},{line,18}]}]}
To do what you'd like to accomplish, you'd need to use a 'proxy' process to receive messages from the socket and send them to another node.
[Socket] ---> [Proxy] ---> |NODE BOUNDARY| ---> [Handler]
Upvotes: 0
Reputation: 13164
Nodes cannot pass sockets between them. If you think about it a moment this should be obvious. A machine is assigned address A, and another address B. A node on A accepts a TCP connection, and then tries to pass the port to B. That can never work because the networking layer of the OS, hardware, and all the network thingies in between believe that TCP connection exists between the machine A and the machine the client is on.
Yes, there are magical ways to make this less true, abstract the network away from the hardware, etc. But that is not the typical case, and none of the infrastructure necessary to make those thing happen are visible to the Erlang VM. It may be true that you are testing on a local machine, but once again, this is not the typical case (not worth supporting as a feature), and the nodes themselves at the VM level would have a lot of difficulty establishing whether it was OK to pass ports among one another safely in a way that worked consistently across platforms (Linux, Windows, OSX, BSD, Solaris, etc.).
So... ports are not things Erlang nodes can pass seamlessly among one another the way they can, say, tuples. The same rule applies to other hardware-bound resources such as open files, process ports, etc.
Upvotes: 3