Как обрабатывать только один клиент одновременно в erlang gen_tcp? - PullRequest
0 голосов
/ 04 февраля 2019

У меня есть TCP-сервер, который прослушивает Ip: Port.

listen(Ip, Port) ->
  Opts = [
    binary,
    {active, false},
    {packet, 0},
    {reuseaddr, true},
    {ip, Ip}
  ],

  case gen_tcp:listen(Port, Opts) of
    {ok, ListenSock} ->
      ?MODULE:loop_accept(ListenSock);
    {error, Reason} ->
      exit(Reason)
  end.

loop_accept(ListenSock) ->
  {ok, Sock} = gen_tcp:accept(ListenSock),
  ?MODULE:loop(Sock),
  ?MODULE:loop_accept(ListenSock).

loop(Sock) ->
  case gen_tcp:recv(Sock, 0) of
    {ok, Data} ->
      gen_tcp:send(Sock, [<<"Response: ">>, Data]),
      ?MODULE:loop(Sock);

    {error, Reason} ->
      ok
  end.

Задача: когда один клиент подключен к Ip: Port (например, telnet Ip Port), другой клиент, пытающийся установить соединение, должен быть сброшен.Другими словами, эксклюзивное использование Ip: Port.

Вопросы:

  • Как это реализовано на Erlang с использованием модуля gen_tcp?
  • Возможно решить с помощью параметровgen_tcp: listen?
  • Как программно сбросить пробное соединение в Erlang?

PS Я новичок в erlang.

1 Ответ

0 голосов
/ 05 февраля 2019

Во-первых, вы не можете recv() так, когда задаете {packet, 0}.Прочитайте этот ответ о gen_tcp .

Сервер может:

  1. Pid = spawn(?MODULE, loop, [Sock])

  2. Мониторпроцесс в # 1:

    Ref = monitor(process, Pid)
    

    Но, чтобы предотвратить состояние гонки, вы должны выполнить # 1 и # 2 в один шаг:

    {Pid, Ref} = spawn_monitor(?MODULE, loop [Sock]) 
    
  3. Послеgen_tcp:accept(ListenSock) выполняет, делает:

    gen_tcp:close(ListenSock)
    
  4. Определить, когда клиент завершает работу и, следовательно, пришло время начать прослушивание для нового клиента:

    receive {'DOWN', Ref, process, Pid, _Reason} -> 
    listen(Ip, Port)
    

    Или,если клиент не завершит работу после завершения отправки данных, вы можете определить, когда клиент закрывает сокет, в loop():

     case gen_tcp:recv(Sock, 0) of
         {ok, Data} ->
             gen_tcp:send(Sock, [<<"Response: ">>, Data]),
             ?MODULE:loop(Sock);
    
         {error, closed} ->
             listen(Ip, Port);
    
         {error, Reason} ->
              ok
      end
    

=====

backlog опция сокета (например, {backlog, 0}):

Опция backlog устанавливает параметр конфигурации сокета ОС.С man listen:

Параметр backlog определяет максимальную длину очереди ожидающих соединений.Если запрос соединения приходит с заполненной очередью, клиент может получить ошибку с указанием ECONNREFUSED.В качестве альтернативы, если базовый протокол поддерживает повторную передачу, запрос может быть проигнорирован, так что повторные попытки могут быть успешными.

И хорошее чтение - это поток в Perl Monks: Сервер TCP: как отклонитькогда соединения заняты? Некоторые фрагменты о конфигурации backlog :

Итак, похоже, что запрос на соединение просто игнорируется (так как TCP поддерживает ретрансляцию)

... когда очередь заполнена, система просто перестает отвечать на пакеты SYN, что вызывает их повторную отправку.В результате узел не получает никаких признаков того, что ваш сервер занят.Он просто продолжает ждать установления соединения.

...