我正在使用以下序列实现简单的tcp服务器:
{ok, LS} = gen_tcp:listen(Port,[{active, true}, {reuseaddr, true}, {mode, list}]),
{ok, Socket} = gen_tcp:accept(LS),
Pid = spawn_link(M, F, [Socket]),
gen_tcp:controlling_process(Socket, Pid) 使用选项{active,true}可能会导致竞争条件,即在调用"controlling_process“之前新的数据包到达套接字进程,这将导致{tcp,Socket,Data}消息到达父进程而不是子进程。
如何避免这种情况?
发布于 2012-07-10 17:24:04
你是正确的。在这种情况下,您肯定需要在侦听套接字选项之间传递{active, false}。考虑下面这段代码:
-define(TCP_OPTIONS, [binary, {active, false}, ...]).
...
start(Port) ->
{ok, Socket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(Socket).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
Pid = spawn(fun() ->
io:format("Connection accepted ~n", []),
enter_loop(Socket)
end),
gen_tcp:controlling_process(Socket, Pid),
Pid ! ack,
accept(ListenSocket);
Error ->
exit(Error)
end.
enter_loop(Sock) ->
%% make sure to acknowledge owner rights transmission finished
receive ack -> ok end,
loop(Sock).
loop(Sock) ->
%% set soscket options to receive messages directly into itself
inet:setopts(Sock, [{active, once}]),
receive
{tcp, Socket, Data} ->
io:format("Got packet: ~p~n", [Data]),
...,
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket ~p closed~n", [Socket]);
{tcp_error, Socket, Reason} ->
io:format("Error on socket ~p reason: ~p~n", [Socket, Reason])
end.因此,在controlling_process成功之前,您不会丢失任何内容。这是一个众所周知的问题,在互联网上已经讨论了很多。如果你想使用ready - to go解决方案,你肯定需要看看Ranch项目。
发布于 2015-11-26 00:14:09
如果套接字是活动的,inet:tcp_controlling_process (由gen_tcp:controlling_process调用)会将套接字设置为被动,然后有选择地接收与该套接字相关的所有消息并将它们发送给新所有者,从而有效地将它们移动到新所有者的消息队列中。然后它将套接字恢复为活动状态。
所以没有竞争条件:他们已经想到了这一点,并在库中修复了它。
发布于 2020-01-06 06:18:06
这绝对是有竞争条件的。我今天刚刚在OTP 21.2中遇到了它,这就是我在这里的原因。数据包可以在accept返回到inet:tcp_controlling_process将套接字设置为被动之间的时间到达。
我只是想指出对@Keynslug上面的答案的微小简化。可以将套接字从非拥有进程设置为活动状态,因此不需要ack消息传递和enter_loop
-define(TCP_OPTIONS, [binary, {active, false}, ...]).
...
start(Port) ->
{ok, Socket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(Socket).
accept(ListenSocket) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
Pid = spawn(fun() ->
io:format("Connection accepted ~n", []),
loop(Socket)
end),
gen_tcp:controlling_process(Socket, Pid),
inet:setopts(Socket, [{active, once}]),
accept(ListenSocket);
Error ->
exit(Error)
end.
loop(Sock) ->
%% set soscket options to receive messages directly into itself
inet:setopts(Sock, [{active, once}]),
receive
{tcp, Socket, Data} ->
io:format("Got packet: ~p~n", [Data]),
...,
loop(Socket);
{tcp_closed, Socket} ->
io:format("Socket ~p closed~n", [Socket]);
{tcp_error, Socket, Reason} ->
io:format("Error on socket ~p reason: ~p~n", [Socket, Reason])
end.https://stackoverflow.com/questions/11409656
复制相似问题