shineworld 73 Posted March 11, 2021 (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: 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 March 11, 2021 by shineworld Share this post Link to post
shineworld 73 Posted March 11, 2021 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. Share this post Link to post
Remy Lebeau 1393 Posted March 11, 2021 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. 1 Share this post Link to post
shineworld 73 Posted March 11, 2021 (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: Edited March 11, 2021 by shineworld Share this post Link to post
Remy Lebeau 1393 Posted March 11, 2021 (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 March 11, 2021 by Remy Lebeau Share this post Link to post
shineworld 73 Posted March 12, 2021 (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 March 12, 2021 by shineworld Share this post Link to post
Remy Lebeau 1393 Posted March 12, 2021 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. 1 Share this post Link to post