Как обрабатывать только один клиент одновременно в erlang gen_tcp?
У меня есть TCP-сервер, который прослушивает Ip: порт.
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 Я новичок в эрланге.
1 ответ
Во-первых, вы не можете recv()
так, когда вы указываете {packet, 0}
, Прочитайте этот ответ о gen_tcp.
Сервер может:
Pid = spawn(?MODULE, loop, [Sock])
Контролируйте процесс в #1:
Ref = monitor(process, Pid)
Но чтобы предотвратить состояние гонки, вы должны выполнить # 1 и #2 за один шаг:
{Pid, Ref} = spawn_monitor(?MODULE, loop [Sock])
После
gen_tcp:accept(ListenSock)
выполняет, делай:gen_tcp:close(ListenSock)
Определите, когда клиент завершает работу, и, следовательно, пришло время начать прослушивание нового клиента:
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, 0}
):
Опция backlog устанавливает параметр конфигурации сокета ОС. От man listen
:
Параметр backlog определяет максимальную длину очереди ожидающих соединений. Если запрос соединения приходит с заполненной очередью, клиент может получить ошибку с указанием ECONNREFUSED. Альтернативно, если базовый протокол поддерживает повторную передачу, запрос может быть проигнорирован, так что повторные попытки могут быть успешными.
И хорошо читать эту ветку в Perl Monks: TCP-сервер: Как отклонить соединения, когда занят? Некоторые фрагменты о конфигурации невыполненных работ:
Таким образом, похоже, что запрос на соединение просто игнорируется (так как TCP поддерживает повторную передачу)
... когда очередь заполнена, система просто перестает отвечать на пакеты SYN, что вызывает их повторную отправку. В результате узел не получает никаких признаков того, что ваш сервер занят. Он просто продолжает ждать установления соединения.