Erlang: Avoiding race condition with gen_tcp:controlling_process

前提是你 提交于 2019-12-31 22:25:17

问题


I am implementing simple tcp server with the following sequence:

{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) 

Using the option {active, true} might cause a race condition where a new packet arrives on the socket process before the "controlling_process" get called , which would result in {tcp,Socket,Data} message arriving to the father proccess instead of the child.

How this could be avoided ?


回答1:


You are right. In such cases you surely need {active, false} passed among listening socket options. Consider this snippet of code:

-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.

Thus you will not lost anything until controlling_process succeeds. It is known problem been discussed a lot over internets. If you wish to use ready to go solution you surely need to take a look at Ranch project.




回答2:


If the socket is active, inet:tcp_controlling_process (called by gen_tcp:controlling_process) sets the socket to passive, then selectively receives all messages related to that socket and sends them to the new owner, effectively moving them to the new owner's message queue. Then it restores the socket to active.

So there's no race condition: they have already thought of that and fixed it in the library.



来源:https://stackoverflow.com/questions/11409656/erlang-avoiding-race-condition-with-gen-tcpcontrolling-process

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!