Работники Erlang udp - хотите иметь больше слушателей udp на одном порту

Я делаю игровой сервер на Erlang и хочу иметь один процесс для каждого подключенного клиента. Предполагается, что этот процесс позаботится обо всех UDP-сообщениях, отправляемых на сервер клиентом, обрабатывая их и отвечая при необходимости.

Как мне этого добиться? Я знаю, что с помощью TCP я могу разместить произвольное количество слушателей для запуска функции.

gen_tcp:accept(ListenSocket)

и он будет блокировать рабочих до тех пор, пока работа не будет доступна, и т. д. Я хочу, чтобы такое же поведение было и с udp, но я пытался сделать простой пример, который порождает новый процесс, который должен соответствовать каждому пакету udp в зависимости от его ip и port, чтобы посмотреть, какой игрок его отправил, забрать себе и проигнорировать остальные.

loop({Sock, Ip, Port}) ->
    receive
        {udp, Sock, Ip, Port, Msg} ->
            do_stuff & loop, etc.
    end
end.

Соответствует ли это IP-адресу от цикла к сообщению? И для того, чтобы это работало, мне нужно, чтобы любой другой прослушиватель UDP не соответствовал этому конкретному пакету, чтобы рабочий процесс мог получать каждое сообщение, которое он должен получать в любой момент времени, верно? Если прослушивает другой «обычный» прослушиватель UDP, он может выбрать сообщение раньше рабочего.

Поэтому я спрашиваю, сработает ли этот подход или я получу сообщение об ошибке, говорящее, что это не владелец сокета или что-то подобное. До сих пор у меня не было ни одного работника, который бы фактически получил какое-либо сообщение, поэтому я не уверен, что это из-за несоответствия или из-за того, что сокет отправляет сообщение только текущему контролирующему процессу, что, я думаю, сделало бы мой план невозможным.


person Simon    schedule 19.05.2014    source источник


Ответы (1)


Название вашего вопроса, а также формулировка предполагают, что вы пытаетесь использовать сокеты UDP, такие как TCP.

TCP и UDP это разные протоколы

TCP — это протокол, ориентированный на соединение. Клиенты подключаются к серверам, и данный сервер будет иметь один сокет для каждого соединения, а также один для прослушивания. Сетевой стек операционной системы направляет входящие пакеты в прослушиваемый сокет или соответствующий подключенный сокет.

UDP не требует подключения. В вашей программе будет только один сокет UDP. Он будет получать все UDP-пакеты на указанный порт. Вы не можете создать процесс и заставить его прослушивать тот же порт. Таким образом, у вас не может быть больше прослушивателей udp на одном и том же порту, как вы пишете в заголовке.

Учитывая реализацию Erlang

Обычный шаблон проектирования для TCP состоит в том, чтобы один процесс обрабатывал прослушивающий сокет, порождая новые рабочие процессы для каждого подключенного сокета.

Поскольку вы пытаетесь реализовать что-то подобное с UDP, вам нужно выполнить то, что сетевой стек делает за вас с TCP. У вас может быть один процесс, принимающий пакеты и пересылающий их соответствующему рабочему процессу, порождая при необходимости на основе исходного порта и IP-адреса. Этот процесс должен поддерживать таблицу всех активных соединений, то есть всех активных потоков пакетов.

Также обратите внимание, что в UDP нет механизма закрытия, поэтому вам следует полагаться на тайм-ауты и, возможно, дополнительный механизм в вашем протоколе.

Фильтрация по одному сокету в каждом процессе, как вы предлагаете в комментариях, невозможна, потому что сокет Erlang в активном режиме отправляет пакеты одному процессу (контролирующему процессу). Вы можете представить себе архитектуру, в которой пакеты широковещательно передаются всем процессам, каждый из которых выполняет фильтрацию для выбора интересных пакетов.

Однако вы увидите, что это не имеет смысла в вашем конкретном сценарии, потому что вам также нужен механизм, чтобы выяснить, следует ли запускать рабочий процесс, и этот механизм в конечном итоге сообщит вам, какой процесс должен обрабатывать входящий пакет. Это могло бы иметь смысл, если бы несколько рабочих процессов обрабатывали одно и то же сообщение.

Давайте конкретно посмотрим, что с некоторым кодом:

server_loop(Workers) ->
    receive
        {udp, Sock, Ip, Port, Msg} = UDPPacket ->
            % find out if we need to spawn a new worker.
            % typically, Workers is a gb_trees:tree().
            NewWorkers = case gb_trees:lookup({Ip, Port}, Workers) of
                none ->
                    NewWorkerPid = spawn_link(fun() -> worker_loop(Ip, Port) end),
                    gb_trees:insert(NewWorkerPid, Workers);
                {value, _WorkerPid} -> Workers  %% <- look, we have the worker!
            end,
            % broadcast to all workers.
            lists:foreach(fun({_, Worker} ->
                Worker ! UDPPacket
            end, gb_trees:to_list(NewWorkers)),
            server_loop(NewWorkers);
        % ... timeout callbacks from workers would go here
    end.

Это просто усложненная версия того, о чем говорилось выше, когда пакет каждый раз отправляется нужному работнику.

server_loop(Workers) ->
    receive
        {udp, Sock, Ip, Port, Msg} = UDPPacket ->
            % find out if we need to spawn a new worker.
            % typically, Workers is a gb_trees:tree().
            {NewWorkers, Worker} = case gb_trees:lookup({Ip, Port}, Workers) of
                none ->
                    NewWorkerPid = spawn_link(fun() -> worker_loop(Ip, Port) end),
                    {gb_trees:insert(NewWorkerPid, Workers), NewWorkerPid}
                {value, WorkerPid} -> {Workers, WorkerPid}
            end,
            % send to worker.
            Worker ! UDPPacket,
            server_loop(NewWorkers);
        % ... timeout callbacks from workers would go here
    end.
person Paul Guyot    schedule 19.05.2014
comment
Я знаю разницу между ними. Я хочу использовать UDP, как если бы это был TCP, так как я хочу, чтобы конкретный процесс обрабатывал конкретного клиента, потому что я буду сопоставлять игроков в игре по IP-адресу, а не по псевдониму (поэтому я не нужно разобрать строку). Я знаю, что это без установления соединения, но это было бы реализовано мной с тайм-аутами и т. Д. Как только игрок войдет в систему, будет создан процесс, который затем будет прослушивать сокет UDP, но будет читать только сообщения, относящиеся к его собственному клиенту. Совпало с IP то есть. Причина этого в том, что я хочу иметь много игроков. - person Simon; 19.05.2014
comment
По сути, вы не ответили на мой вопрос. Мой вопрос касался сопоставления с образцом. Будет ли работать сопоставление? Или только одному процессу разрешено получать сообщения через указанный сокет, а это означает, что я не могу просто передать его рабочим? - person Simon; 19.05.2014
comment
Извините за недостаточно ясность. Ваш сервер будет иметь только один сокет UDP, и предлагаемая вами реализация невозможна. Ваш вопрос о сопоставлении с шаблоном на самом деле не имеет отношения к делу, но да, сам шаблон будет работать для фильтрации входящих пакетов. Вы можете сопоставлять связанные переменные (здесь Sock, Ip и Port). Однако это плохая идея, так как все нефильтрованные пакеты останутся и будут накапливаться в очереди сообщений. - person Paul Guyot; 19.05.2014
comment
Но об этом можно было бы позаботиться по-другому. Меня просто интересует, возможен ли такой подход технически. Фильтрация сообщений в одном сокете, но из разных процессов. Так что это связано, потому что на самом деле мой вопрос заключался в том, могу ли я использовать разные процессы для фильтрации в одном и том же сокете. В любом случае, это был мой вопрос, поэтому, если это было неясно, извините. Все остальные неотфильтрованные сообщения будут обработаны основным процессом. - person Simon; 19.05.2014
comment
Невозможно, чтобы несколько процессов получали сообщение из одного сокета UDP. - person Paul Guyot; 19.05.2014
comment
Я бы хотел, чтобы вы на самом деле не вдавались в подробности о том, как решить проблему, потому что это зависит от меня, чтобы сделать это самостоятельно. И у меня уже был план, как это сделать на случай, если то, о чем я действительно просил, сработает. Так что на самом деле нет смысла добавлять туда этот код, потому что вы просто предполагали, что я решу его не так, как собирался. Таким образом, добавление этого кода не имеет значения. Если бы вы были так любезны, чтобы сделать ответ на мой реальный вопрос более заметным, я был бы признателен. Этот вопрос: а) Могу ли я слушать от разных рабочих на одном и том же сокете UDP? и б) Будет ли работать фильтрация? - person Simon; 20.05.2014