Название вашего вопроса, а также формулировка предполагают, что вы пытаетесь использовать сокеты 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