10

私は次のシーケンスで単純な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}メッセージが父親のプロセスではなく父親のプロセスに到着します。子。

これをどのように回避できますか?

4

3 に答える 3

17

あなたが正しいです。そのような場合、確かに{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したがって、成功するまで何も失うことはありません。インターネット上でよく議論されている既知の問題です。すぐに使用できるソリューションを使用したい場合は、Ranchプロジェクトを確認する必要があります。

于 2012-07-10T09:24:04.157 に答える
2

ソケットがアクティブな場合、inet:tcp_controlling_process( によって呼び出されgen_tcp:controlling_processます) はソケットをパッシブに設定し、そのソケットに関連するすべてのメッセージを選択的に受信して新しい所有者に送信し、それらを新しい所有者のメッセージ キューに効果的に移動します。次に、ソケットをアクティブに戻します。

したがって、競合状態はありません。彼らはすでにそれを考えており、ライブラリで修正しています。

于 2015-11-25T16:14:08.970 に答える
2

絶対に競合状態があります。今日、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.
于 2020-01-05T22:18:05.620 に答える