delphiudpindyindy10

Using Indy10, UDP server return response being sent by a different IP


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?


Solution

  • 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;