Erlang: Avoiding race condition with gen_tcp:controlling_process

后端 未结 3 933
抹茶落季
抹茶落季 2021-02-05 23:42

I am implementing simple tcp server with the following sequence:

{ok, LS} = gen_tcp:listen(Port,[{active, true}, {reuseaddr, true}, {mode, list}]),
{ok, Socket}          


        
3条回答
  •  礼貌的吻别
    2021-02-06 00:22

    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.

提交回复
热议问题