MultiCast Messages to multiple clients on the same machine
Asked Answered
W

5

10

Im trying to write a server/service that broadcasts a message on the lan ever second or so, Kind of like a service discovery.

The message needs to be received by multiple client programs that could be on the same machine or different machines. But there could be more than one program on each machine running at the same time.

Im using delphi7, with indy 9.0.18

where im stuck is if i should be using UDP(TIdUDPClient/Server) or IP MultiCast (TIdIPMCastClient/Server) or if its even possible...

Ive managed to get it to work with IP Multi Cast with one client per machine, but even after many trys with different bindings.. max/min ports etc, i cant seem to find a solution.

Wieren answered 9/4, 2010 at 2:50 Comment(0)
V
8

I think you're looking for the SO_REUSEADDR socket option. Setting that option on a socket allows multiple sockets to listen on the same port. For multicast Windows guarantees that the message will be delivered to all sockets (otherwise the message only goes to one socket, randomly).

You usually do this by calling setsockopt, but I'm not a Delphi developer so I'm not sure what your API looks like. This question seems to show an example of someone doing something similar in Delphi.

Vernita answered 14/4, 2010 at 3:19 Comment(2)
Excellent, SO_REUSEADDR thats the hint i neededWieren
On mac OSX, I had to add: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)Essay
D
5

I have never done this, but it seems like "mailslots" is what you need. It will broadacast a message on the local network, and receive any replies from other workstations that know how to reply. This is how the popular "armadillo" license manager works (for making sure that registration keys aren't "over-subscribed"). My app (ClipMate) uses Armadillo to as a protection wrapper (shareware wrapper). When a registered user runs the app, it checks to see if that same key is in use by other machines on the same network. It basically says: "I'm using license 1234, how about you?" It waits for replies (i do this in a separate thread during startup so I don't block my startup). If other workstations report back that they're using the same key, I check the count against the number of seats contained in the license. I'm not entirely sure that it's as robust on Windows7....

Dupondius answered 9/4, 2010 at 3:16 Comment(3)
A simple mail slot implementation exists at funkypuppy.com, it works great in Delphi 7.Mortar
Mail slots did sound promising. i tried the implementation from funkypuppy but i cannot seem to have more than 2 programs receiving messages on the same computer. Maybe im doing it wrong?Wieren
Although Windows mailslots are 'advertised' as a broadcast mechanism, that refers to receivers on different machines. On a single machine only one process can own and read a mailslot of a particular name.Premillenarian
L
2

It's definitely possible.

Re "UDP or multicast", you're talking apples and oranges. Multicast is an IP concept, so you can happily UDP over multicast IP, or over broadcast IP.

If you're OK with the limitation of having all the clients link-local (routers etc. generally don't forward broadcast packets), I'd say just go with broadcast. TIdUdpBase.Broadcast will be your friend here.

Update: With either multicast or broadcast, you can only have one socket bound to any particular IP/port pair. Thus, if you want multiple clients all listening to the SAME broadcast/multicast, I think you will need an extra dispatcher client. This dispatcher client receives broadcasts and notifies every client on the machine.

Within each of your clients you have a little registration procedure that says "Try bind to the port to which broadcasts are sent. If you can, set up a dispatcher client on that port. If you can't, the dispatcher's already created, and register yourself to that dispatcher."

That registration process could be as simple as binding to any available port on the localhost IP, and saying to the dispatcher "Please send broadcasts to this IP/port."

Update: Christopher Chase has the right idea. I just finished almost the exact same solution as his, except I patched IdIPMCastClient, adding a property ReuseAddr: Boolean and changing TIdIPMCastClient.GetBinding by adding

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

between the calls to AllocateSocket and Bind (where SetReuseAddr: Integer).

Lolanthe answered 12/4, 2010 at 14:35 Comment(2)
Thanks for the answer, The limitations of UDP and multicast is actually required, ie i don't want the messages to leave the lan etc, But the Problem i cant seem to solve is having two clients on the same computer listening for broadcastsWieren
I have added a new ReuseSocket property to TIdIPMCastClient in Indy 10.Horrified
H
1

RemObjects has a nice solution for this: ROZeroConf

Before that was available, I made something like that myself with TROBroadcastChannel of RemObjects SDK (based UDP and Indy). Inside that component, it calls TIdUDPBase.Broadcast to send and TIdUDPClient.ReceiveBuffer to receive responses.

(btw, UDP broadcast only works on the same network/subnet, ROZeroConf is a better solution)

Hyetography answered 9/4, 2010 at 6:22 Comment(0)
W
1

with the hint from shf301, this is the code i got it to work with

i created a new TIdIPMCastClient

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

added the Procedure

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;

copied the GetBinding code from TIdIPMCastClient, and added the SetReUseAddr before the bind

  Bindings[i].AllocateSocket(Id_SOCK_DGRAM);
  SetReUseAddr(Bindings[i], True);
  Bindings[i].Bind;
Wieren answered 14/4, 2010 at 6:23 Comment(1)
I have added a new ReuseSocket property to TIdIPMCastClient in Indy 10.Horrified

© 2022 - 2024 — McMap. All rights reserved.