Многоадресные сообщения нескольким клиентам на одном компьютере - PullRequest
10 голосов
/ 09 апреля 2010

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

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

Я использую delphi7, с инди 9.0.18

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

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

Ответы [ 5 ]

8 голосов
/ 14 апреля 2010

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

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

5 голосов
/ 09 апреля 2010

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

2 голосов
/ 12 апреля 2010

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

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

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

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

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

Этот процесс регистрации может быть таким же простым, как привязка к любому доступному порту на локальном 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).

1 голос
/ 14 апреля 2010

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

я создал новый 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;
1 голос
/ 09 апреля 2010

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

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

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

...