Erlang: избежание состояния гонки с gen tcp:процесс управления

я реализую простой 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}, поступающему в отцовский процесс вместо дочернего.

как этого можно избежать ?

2 ответов


вы правы. В таких случаях вам обязательно нужно {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, вам обязательно нужно взглянуть на Ранч.


Если сокет активен,inet:tcp_controlling_process (называемых gen_tcp:controlling_process) устанавливает сокет в пассивный, затем выборочно получает все сообщения, связанные с этим сокетом, и отправляет их новому владельцу, эффективно перемещая их в очередь сообщений нового владельца. Затем он восстанавливает сокет в active.

таким образом, нет никакого условия гонки: они уже подумали об этом и исправили его в библиотеке.