Erlang: Avoiding race condition with gen_tcp:controlling_process

后端 未结 3 923
抹茶落季
抹茶落季 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:09

    There absolutely is a race condition. I just encountered it, today, in OTP 21.2, and that's why I'm here. A packet can arrive between the time that accept returns and the time that inet:tcp_controlling_process sets the socket to passive.

    I just wanted to point out tiny simplification to @Keynslug's answer above. The socket can be set to active from a non-owning process, so the ack messaging and enter_loop are unnecessary

    -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.
    
    0 讨论(0)
  • 2021-02-06 00:15

    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.

    0 讨论(0)
  • 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.

    0 讨论(0)
提交回复
热议问题