Невозможно принять соединения на сокете при создании сокетов на удаленном узле через RPC в Erlang

Я изо всех сил пытаюсь определить причину, по которой gen_tcp:accept всегда возвращает ответ {ошибка, закрыто}.

По сути, у меня есть супервизор, который создает прослушивающий сокет:

gen_tcp:listen(8081, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),

Затем этот сокет передается дочернему элементу, который является реализацией поведения gen_server. Затем дочерний элемент принимает соединения на сокете.

accept(ListeningSocket, {ok, Socket}) ->                                   
    spawn(fun() -> loop(Socket) end),                                      
    accept(ListeningSocket);
accept(_ListeningSocket, {error, Error}) ->
    io:format("Unable to listen on socket: ~p.~n", [Error]),
    gen_server:call(self(), stop).

accept(ListeningSocket) ->                                                 
    accept(ListeningSocket, gen_tcp:accept(ListeningSocket)).                                                                                             

loop(Socket) ->                                                            
    case gen_tcp:recv(Socket, 0) of                                        
        {ok, Data} ->                                                      
            io:format("~p~n", [Data]),                                     
            process_request(Data),                                         
            gen_tcp:send(Socket, Data),                                    
            loop(Socket);                                                  
        {error, closed} -> ok                                              
   end.

Я загружаю бинарные файлы супервизора и gen_server BEAM локально и загружаю их на другой узел (который работает на той же машине) с помощью RPC-вызова code:load_binary. Затем я запускаю супервизор через вызов RPC, который, в свою очередь, запускает сервер. {error,closed} всегда возвращается gen_tcp:accept в этом сценарии.

Должен ли я запускать супервизор и сервер при входе в оболочку узла, тогда сервер может принимать соединения без проблем. Это включает в себя «remsh» для удаленного узла, который не смог бы принимать соединения, если бы я ранее выполнил RPC для неудачного запуска сервера.

Кажется, я могу воспроизвести проблему, используя только оболочку:

[Terminal 1]: erl -sname node -setcookie abc -distributed -noshell

[Terminal 2]: erl -sname rpc -setcookie abc:

              net_adm:ping('node@verne').
              {ok, ListeningSocket} = rpc:call('node@verne', gen_tcp, listen, [8081, [binary, {packet, 0}, {active, true}, {reuseaddr, true}]]).
              rpc:call('node@verne', gen_tcp, accept, [ListeningSocket]).

Ответ на окончательный RPC: {ошибка, закрыто}.

Может ли это быть как-то связано с владением сокетом/портом?

Если это поможет, нет клиентов, ожидающих подключения, и я нигде не устанавливаю тайм-ауты.


person user3813812    schedule 03.07.2015    source источник


Ответы (1)


Каждый rpc:call запускает новый процесс на целевом узле для обработки запроса. В вашем последнем примере ваш первый вызов создает сокет прослушивания внутри такого процесса, и когда этот процесс умирает в конце вызова rpc, сокет закрывается. Таким образом, ваш второй вызов rpc для попытки принятия завершается неудачей из-за уже закрытого сокета для прослушивания.

Ваш дизайн кажется необычным по нескольким причинам. Например, ненормально, когда супервизоры открывают сокеты. Вы также говорите, что ребенок является gen_server, но показываете ручной цикл recv, который, если запустить его внутри gen_server, заблокирует его. Вместо этого вы можете объяснить, чего вы пытаетесь достичь, и попросить помощи в разработке дизайна для достижения ваших целей.

person Steve Vinoski    schedule 03.07.2015
comment
Это имеет смысл, Стив. Спасибо за объяснение. - person user3813812; 04.07.2015
comment
Эта проблема возникла некоторое время назад, когда я экспериментировал с сокетами в Erlang. Я просто не удосужился задать этот вопрос раньше. Мое первоначальное намерение, если я правильно помню, состояло в том, чтобы несколько gen_servers принимали соединения на одном и том же сокете. Тот факт, что на целевом узле запускается новый процесс для обработки запроса, объясняет, почему сокет немедленно закрывается при запуске gen_server через RPC. - person user3813812; 04.07.2015