Erlang udp работници - искат да имат повече udp слушатели на същия порт

Създавам сървър за игри в Erlang и искам да мога да имам един процес за всеки свързан клиент. Този процес трябва да се грижи за всички UDP съобщения, изпратени до сървъра от клиента, като ги обработва и отговаря, ако е необходимо.

Как да постигна това? Знам, че с TCP мога да поставя произволен брой слушатели, които да изпълняват функцията

gen_tcp:accept(ListenSocket)

и ще блокира работниците, докато работата е налична и т.н. Искам това поведение и с udp, но се опитвах да направя прост пример, който ражда нов процес, който трябва да съответства на всеки udp пакет в зависимост от неговия ip и порт, за да видите кой играч го е изпратил, вземете тези за себе си и игнорирайте останалите.

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 вместо по nick (така че не трябва да анализирате низа). Знам, че е без връзка, но това ще бъде приложено от мен с изчаквания и т.н. След като играч влезе, ще се зареди процес, който след това ще слуша 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