Jump to content
shineworld

INDY UDP broadcast to a specific Network

Recommended Posts

Posted (edited)

Hi all,
I've migrated my application to the latest Indy10 sources.

A very good library, congrats to developers.

 

Now I would like to improve my research devices on the net feature.

In few words, my IoT devices implement a UDP service to respond to a broadcast UDP request of identification.

When IoT sends a reply to discover software uses the IoT IP address so after I can, for example, use other messages to change the IoT device programmed IP.

 

Actually, the system works BUT depends on what network adapter will route the discover software broadcast message.

For example, I've collected the Network Adapters in my system before to send discover UPD packet as a broadcast message:

image.png

 

At this point clicking on the Change IP button a form make the broadcast search using this code:
 

procedure TConfiguratorView.SearchClick(Sender: TObject);
var
  S: string;

  procedure AddInteger(var Buffer: string; Value: Integer);
  var
    ValueData: array [0..3] of Byte absolute Value;
  begin
    Buffer := Buffer + Char(ValueData[3]);
    Buffer := Buffer + Char(ValueData[2]);
    Buffer := Buffer + Char(ValueData[1]);
    Buffer := Buffer + Char(ValueData[0]);
  end;

begin
  // shows advice message about network routing question
  if Sender <> nil then
  begin
    case ShowCustomMessage
    (
      _('Info About Search'),
      _('Select desired option:'),
      _('Search Message could be routed by OS in a wrong Network Interface so if, after a Search, you don''t find any ' +
        'CNC Board try again disabling any Network Interface not connected with the CNC Board.\n\n' +
        'PS: If you are in a Virtual Machine the Network Adapter must be in BRIDGED MODE.'),
      '',
      [
        _('Continue'),
        _('Abort')
      ]
    ) of
      101: Exit;
    end;
  end;

  // stores serial number selected
  if GetActiveQMoveInfo = nil then
    FSerialNumberSelected := 0
  else
    FSerialNumberSelected := GetActiveQMoveInfo.SerialNumber;

  // clears qmove list content
  QMoveListClear;

  // inits and starts searching phase
  FUDPServer.Bindings.Clear;
  with FUDPServer.Bindings.Add do
  begin
    IP := '0.0.0.0';
    Port := 5001;
  end;
  FUDPServer.Active := True;
  SearchTimer.Interval := SEARCH_TIME;
  SearchTimer.Enabled := True;

  // sends broadcast find command
  if FSerialNumber = 0 then
    FUDPServer.Broadcast(QMOVE_ETHERNET_HEAD + Char(QMOVE_ETHERNET_VERSION) + QMOVE_EHTERNET_FIND_COMMAND, QMOVE_ETHERNET_SEARCHING_PORT, '', IndyTextEncoding(enc8Bit))
  else
  begin
    S :=
    (
      QMOVE_ETHERNET_HEAD +
      Char(QMOVE_ETHERNET_VERSION) +
      QMOVE_EHTERNET_FIND_COMMAND +
      #226#64
    );
    AddInteger(S, FSerialNumber);
    FUDPServer.Broadcast(S, QMOVE_ETHERNET_SEARCHING_PORT, '', IndyTextEncoding(enc8Bit));
    FSerialNumber := 0;
  end;

  // refreshes objects
  RefreshObjects;
end;

When the PC has only one Network Adapter (eg: the LAN) the system work always.
When there are more than one Network Adapter depends on where the windows "route" the UDP packets so I need to require to end-user
to manually disable temporarily any other Network Adapter (eg: WIFI, VMWare Network Adapters, etc.).

There is a way to specify to UDP server to send a broadcast to a specific network adapter (eg: 192.168.x.x) instead to use an unknown default?

Thank you in advance for your suggestions
Silverio

 

Edited by shineworld

Share this post


Link to post

I guess to have solved!
I'm very dumb in network things, so I can mistake anything.

 

Calling:

FUDPServer.Broadcast(S, QMOVE_ETHERNET_SEARCHING_PORT, '', IndyTextEncoding(enc8Bit));

The library changes the IP destination address ' ' as 255.255.255.255 and uses the default route adapter from the Windows OS list.

If I change the ' ' with the Broadcast IP of the desired network adapter then the packet is routed correctly and I can get a reply from devices in that net.


image.png.f15b9743a566585326064eecab5e7ec0.png

Share this post


Link to post
2 hours ago, shineworld said:

If I change the ' ' with the Broadcast IP of the desired network adapter then the packet is routed correctly and I can get a reply from devices in that net.

Yes, you could specify the broadcast IP of the desired network, and let Windows figure out which adapter to use.

 

Alternatively, rather than creating only 1 Binding that is bound to '0.0.0.0', you can create multiple Bindings, one for each network adapter (looks like you are already enumerating the adapters anyway), and then you can call Broadcast() on each adapter directly.  That will allow you to search multiple networks at a time.

  • Like 1

Share this post


Link to post
Posted (edited)

I've tried to place some number (IP Broadcast for the network adapter in Binding but I gel always a socket error):

  // inits and starts searching phase
  FUDPServer.Active := False;
  FUDPServer.Bindings.Clear;
  with FUDPServer.Bindings.Add do
  begin
    IP := '192.168.0.255';
    Port := 5001;
  end;
  with FUDPServer.Bindings.Add do
  begin
    IP := '192.168.120.255';
    Port := 5001;
  end;
  FUDPServer.BroadcastEnabled := True;
  FUDPServer.Active := True;

The resulting approach, at moment, was to define in what network adapter has to perform the scan:

image.thumb.png.f79178338bb25efe33950114aba59602.png

Edited by shineworld

Share this post


Link to post
Posted (edited)
3 hours ago, shineworld said:

I've tried to place some number (IP Broadcast for the network adapter in Binding but I gel always a socket error)

You can't bind a socket to a broadcast IP, you can only send data to a broadcast IP as the destination.  You must bind the socket to the adapter's local network IP instead, eg;

// inits and starts searching phase
FUDPServer.Active := False;
FUDPServer.Bindings.Clear;
with FUDPServer.Bindings.Add do
begin
  IP := '192.168.0.27';
  Port := 5001;
end;
...
FUDPServer.BroadcastEnabled := True;
FUDPServer.Active := True;

And you can't bind a socket to an IP that does not belong to the local machine.  In your screenshots, there is no local adapter present that has a local IP on the '192.168.120.x' subnet (unless you are filtering the adapters and we just can't see them all).

Edited by Remy Lebeau

Share this post


Link to post
Posted (edited)

Sorry I've made a mistake to write a demo code manually.
What I have:

- A PC with many network adapters (actually there are 4 LAN ports, 1 wifi, and adapters from VMWare and Virtual Box).

- The IoT devices will be placed by customers in some of the available Network Adapters (I don't know what and perhaps also the customers don't know exactly what LAN will be).
- Any IoT device has a UDP client who listening to a port (1460) for discovery.
- Any IoT device has IP/MASK/IP Ports and TCP Clients ports bound to them.

- A user starts the IoT device Discovery in all the net.

- The Discovery System uses a TIdUPDServer bind to the port 5001 to any net - no net address (0.0.0.0) to receive any UDP packet from any network returning to the 5001 port.

 

At this point, I can call:

for I := 0 to FNetworksList.Count - 1 do
  FUDPServer.Broadcast(S, QMOVE_ETHERNET_SEARCHING_PORT, FNetworksList[I].BroadCastIPForThisNA , IndyTextEncoding(enc8Bit));

The UDP Server sent the UDP discovery packet to any Network Adapter and if in the network adapter there is an IoT device it will respond to discovery software
using a UDP packet at port 5001 (where discovery software is listening for any net) using the destination IP (where discovery software lives) contained in the received discovery packet.

OK, this works perfectly now.
At this point I would like to understand what you wrote:

 

Quote

Alternatively, rather than creating only 1 Binding that is bound to '0.0.0.0', you can create multiple Bindings, one for each network adapter (looks like you are already enumerating the adapters anyway), and then you can call Broadcast() on each adapter directly.  That will allow you to search multiple networks at a time.


I'm sorry for the question, I'm an occasional user of network things, and I don't have any deep knowledge background in the matter.

Edited by shineworld

Share this post


Link to post
9 hours ago, shineworld said:

At this point, I can call:


for I := 0 to FNetworksList.Count - 1 do
  FUDPServer.Broadcast(S, QMOVE_ETHERNET_SEARCHING_PORT, FNetworksList[I].BroadCastIPForThisNA , IndyTextEncoding(enc8Bit));

OK, this works perfectly now.

If the OS can broadcast to multiple subnets using a single socket bound to local IP 0.0.0.0, then so be it.  I just know that hasn't always worked that way.  But maybe things are better in recent years.

9 hours ago, shineworld said:

At this point I would like to understand what you wrote:

If there are multiple adapters installed, and you know what IP they are assigned, you can bind a separate socket to each one directly, eg:

FUDPServer.Active := False;
FUDPServer.Bindings.Clear;
...
for I := 0 to FNetworksList.Count - 1 do
begin
  with FUDPServer.Bindings.Add do
  begin
    IP := FNetworksList[I].LocalIP;
    Port := 5001;
  end;
end;
...
FUDPServer.BroadcastEnabled := True;
FUDPServer.Active := True;

This way, if you then Send...()/Broadcast() on any given Binding, it will send out only to that specific adapter/subnet directly, rather than the OS having to hunt around the routing tables to figure out which adapter/subnet to send out to, as is the case when calling Send...()/Broadcast() on a single Binding that is bound to 0.0.0.0.

 

Operating Systems are pretty good at maintaining good routing tables, but they are not infallible.  Sometimes routes are not configured properly, or get corrupted over time.  So sometimes it may be easier/better to just be more direct and cut out the middle man.

  • Like 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×