Я пытаюсь записать сервер/сервис, который широковещательно передает сообщение на LAN, когда-либо приблизительно второй, Отчасти как сервисное исследование.
Сообщение должно быть получено несколькими клиентскими программами, которые могли быть на той же машине или различных машинах. Но могла быть больше чем одна программа на каждой машине, работающей одновременно.
Я использую delphi7 с инди 9.0.18
то, где я застреваю, - то, если я должен использовать UDP (TIdUDPClient/Server) или Многоадресный IP-пакет (TIdIPMCastClient/Server) или если его даже возможный...
Мне удалось заставить это работать с IP Много Бросок с одним клиентом на машину, но даже после многих попыток с различной привязкой.. макс. порты / минимальные порты и т.д., я, может казаться, не нахожу решение.
Думаю, вы ищете вариант сокета SO_REUSEADDR . Установка этой опции для сокета позволяет нескольким сокетам прослушивать один и тот же порт. Для многоадресной рассылки Windows гарантирует, что сообщение будет доставлено во все сокеты (в противном случае сообщение попадает только в один сокет случайным образом).
Вы обычно делаете это, вызывая setsockopt, но я не разработчик Delphi, поэтому я не уверен, как выглядит ваш API. Этот вопрос , кажется, показывает пример того, как кто-то делает нечто подобное в Delphi.
У RemObjects есть хорошее решение для этого: ROZeroConf
До того, как это стало доступно, я сам делал что-то подобное с TROBroadcastChannel
RemObjects SDK (на основе UDP и Indy). {{1 }} Внутри этого компонента он вызывает TIdUDPBase.Broadcast
для отправки и TIdUDPClient.ReceiveBuffer
для получения ответов.
(кстати, широковещательная рассылка UDP работает только в той же сети / подсети, ROZeroConf - лучшее решение)
Я никогда этого не делал, но похоже, что вам нужны "почтовые ящики". Он будет транслировать сообщение по локальной сети и получать ответы от других рабочих станций, которые знают, как отвечать. Так работает популярный менеджер лицензий «броненосец» (чтобы убедиться, что регистрационные ключи не «переподписаны»). Мое приложение (ClipMate) использует Armadillo как защитную оболочку (условно-бесплатную оболочку). Когда зарегистрированный пользователь запускает приложение, он проверяет, используется ли тот же ключ другими машинами в той же сети. По сути, он говорит: «Я использую лицензию 1234, как насчет вас?» Он ждет ответов (я делаю это в отдельном потоке во время запуска, чтобы не блокировать свой запуск). Если другие рабочие станции сообщают, что используют тот же ключ, я сверяю счет с количеством рабочих мест, содержащихся в лицензии. Я не совсем уверен, что он такой же надежный в Windows7 ....
Это определенно возможно.
По поводу «UDP или многоадресной рассылки» вы говорите о яблоках и апельсинах. Многоадресная рассылка - это концепция 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).
с подсказкой от 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;