Сообщения MultiCast нескольким клиентам на одной машине

Я пытаюсь написать сервер/службу, которая транслирует сообщение на lan за все время или около того, вроде как обнаружение службы.

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

Im, использующий delphi7, с indy 9.0.18

где я застрял, если я должен использовать UDP (TIdUDPClient/Server) или IP MultiCast (TIdIPMCastClient/Server), или если это возможно...

Мне удалось заставить его работать с IP Multi Cast с одним клиентом на машину, но даже после многих попыток с различными привязками. max/min ports и т.д., я не могу найти решение.

Ответ 1

Я думаю, что вы ищете опцию SO_REUSEADDR. Установка этой опции в сокете позволяет нескольким сокетам прослушивать один и тот же порт. Для многоадресной рассылки Windows гарантирует, что сообщение будет доставлено во все сокеты (иначе сообщение будет отправляться только в один сокет, случайным образом).

Обычно вы делаете это, вызывая setockopt, но я не разработчик Delphi, поэтому я не уверен, как выглядит ваш API. Этот question, кажется, показывает пример того, кто делает что-то подобное в Delphi.

Ответ 2

Я никогда этого не делал, но похоже, что "mailslots" - это то, что вам нужно. Он будет широко распространять сообщение в локальной сети и получать ответы от других рабочих станций, которые знают, как отвечать. Вот как работает популярный менеджер лицензий "armadillo" (для обеспечения того, чтобы регистрационные ключи не были "перерегистрированы" ). Мое приложение (ClipMate) использует Armadillo в качестве защитной оболочки (shareware wrapper). Когда зарегистрированный пользователь запускает приложение, он проверяет, используется ли тот же ключ другими машинами в той же сети. В основном он говорит: "Я использую лицензию 1234, а как насчет вас?" Он ждет ответов (я делаю это в отдельном потоке во время запуска, поэтому я не блокирую запуск). Если другие рабочие станции сообщают, что они используют один и тот же ключ, я проверяю счет на количество мест, содержащихся в лицензии. Я не совсем уверен, что он такой же надежный на Windows7....

Ответ 3

Это определенно возможно.

Re "UDP или multicast", вы говорите о яблоках и апельсинах. Многоадресная рассылка - это концепция IP, поэтому вы можете наслаждаться UDP через многоадресный IP-адрес или через широковещательный IP-адрес.

Если вы в порядке с ограничением наличия у всех клиентов link-local (маршрутизаторы и т.д., как правило, не пересылают широковещательные пакеты), я бы сказал, просто пойдите с трансляцией. TIdUdpBase.Broadcast будет вашим другом здесь.

Обновление: При использовании многоадресной рассылки или широковещательной передачи вы можете иметь только один сокет, привязанный к какой-либо конкретной паре IP/порта. Таким образом, если вы хотите, чтобы несколько клиентов все прослушивали SAME широковещательную/многоадресную рассылку, я думаю, вам понадобится дополнительный диспетчерский клиент. Этот диспетчерский клиент получает широковещательные сообщения и уведомляет каждого клиента на машине.

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

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

Обновление: Кристофер Чейз имеет правильную идею. Я только что закончил почти то же самое решение, что и его, за исключением того, что я исправил IdIPMCastClient, добавив свойство ReuseAddr: Boolean и изменив TIdIPMCastClient.GetBinding, добавив

if Self.ReuseAddr then begin
  SetReuseAddr := Id_SO_True;
  Bindings[i].SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, @SetReuseAddr, Sizeof(SetReuseAddr));
end;

между вызовами AllocateSocket и Bind (где SetReuseAddr: Integer).

Ответ 4

RemObjects имеет приятное решение для этого: ROZeroConf

До того, как это было доступно, я сделал что-то подобное себе с TROBroadcastChannel RemObjects SDK (на основе UDP и Indy). Внутри этого компонента он вызывает TIdUDPBase.Broadcast для отправки и TIdUDPClient.ReceiveBuffer для получения ответов.

(btw, широковещательная передача UDP работает только в той же сети/подсети, ROZeroConf - лучшее решение)

Ответ 5

с подсказкой от shf301, это код, который я получил, чтобы он работал с

i создал новый TIdIPMCastClient

 TIdReUseIPMCastClient = class(TIdIPMCastClient)
  private
    procedure SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean);
  protected
    function GetBinding: TIdSocketHandle; override;
  public
  end;

добавлена ​​процедура

procedure TIdReUseIPMCastClient.SetReUseAddr(InBinding: TIdSocketHandle; const Value: boolean);
var
  tempi: integer;
begin
  if Assigned(InBinding) and InBinding.HandleAllocated then
    begin
    tempi := iif(Value, 1, 0);
    InBinding.SetSockOpt(Id_SOL_SOCKET, Id_SO_REUSEADDR, PChar(@tempi), SizeOf(tempi));
    end;
end;

скопировал код GetBinding из TIdIPMCastClient и добавил SetReUseAddr перед связыванием

  Bindings[i].AllocateSocket(Id_SOCK_DGRAM);
  SetReUseAddr(Bindings[i], True);
  Bindings[i].Bind;