I finally resolved today why simple UDP client/server text communications to a server was producing an error. The server has 2 IPv4 addresses. I was sending to one of the IP addresses and TIdUDPServer was sending back on the other! This had me stumped for weeks.
Why does the UDPServer bind to the wrong IP address for sending a response?
Client code
UDPClient.Host := 'an IPv4 address';
UDPClient.IP := 9999;
UDPClient.Send('Hello');
Memo2.Text := UDPClient.ReceiveString(3000);
Server code
UDPServer.DefaultPort := 9999;
UDPServer.Active := True;
procedure TForm1.UDPServerUDPRead(AThread: TIdUDPListenerThread;
AData: TIdBytes; ABinding: TIdSocketHandle);
begin
Memo1.text := BytesToString(AData);
ABinding.SendTo(ABinding.PeerIP,ABinding.PeerPort,'Received: '+Memo1.Text);
end;
Is there a solution, or would I have to send the IP address to bind to in the original text message?
The code shown is fine. But, if your server has only a single Binding
that is bound to 0.0.0.0
then it will listen for inbound packets on all local IPv4 interfaces, but then you are at the mercy of the OS regarding which IPv4 interface it decides to use when sending outbound packets, as 0.0.0.0
is not a valid source IP for a packet.
Since the OS is picking the other IPv4 interface that you don't want, you will have to configure the Binding
to listen only on the specific IP that you actually want to use, eg:
UDPServer.Active := False;
UDPServer.Bindings.Clear;
UDPServer.DefaultPort := 9999;
UDPServer.Bindings.Add.SetBinding(DesiredIPAddress, UDPServer.DefaultPort, Id_IPv4);
UDPServer.Active := True;
Alternatively, if you do want to listen on multiple interfaces, and ensure that each interface sends out using its respective IP, then you will have to create a separate Binding
for each interface individually, eg:
UDPServer.Active := False;
UDPServer.Bindings.Clear;
UDPServer.DefaultPort := 9999;
UDPServer.Bindings.Add.SetBinding(IPAddress1, UDPServer.DefaultPort, Id_IPv4);
UDPServer.Bindings.Add.SetBinding(IPAddress2, UDPServer.DefaultPort, Id_IPv4);
// etc...
UDPServer.Active := True;
You can optionally use the TIdStack.GetLocalAddressList()
method to find all of local IPs if needed, eg:
var
list: TIdStackLocalAddressList;
i: Integer;
...
UDPServer.Active := False;
UDPServer.Bindings.Clear;
UDPServer.DefaultPort := 9999;
list := TIdStackLocalAddressList.Create;
try
GIdStack.GetLocalAddressList(list);
for i := 0 to list.Count-1 do
UDPServer.Bindings.Add.SetBinding(list[i].IPAddress, UDPServer.DefaultPort, list[i].IPVersion);
finally
list.Free;
end;
UDPServer.Active := True;