Search code examples
delphiudpdelphi-7indy

Reading data with TIdUDPServer


I would like to read data from an electronic device that sends data to my PC by UDP. I have programmed a UDP server in Delphi. An exception occurs in class EIdSocketError (Failure #10049). Here is my code:

procedure TForm1.Button1Click(Sender: TObject);
begin
   IdUDPServer1.Bindings.add.IP := '192.168.1.1';  //Electronic device ip
   IdUDPServer1.Bindings.add.Port:= 49152;         //Electronic device port   
   IdUDPServer1.OnUDPRead:= UDPRead;
   IdUDPServer1.Active:=True;

end;

procedure TForm1.UDPRead (Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
var
   bytes_received: integer;
begin
   bytes_received:=AData.size;
end;

What am I doing wrong?

Thanks in advance


Solution

  • As Keith Miller stated, you are misusing the Bindings property. You are adding 2 bindings to the server - one for 192.168.1.1 on port 0 and another for 0.0.0.0 on port 49152. You need to call Bindings.Add() only once for each IP/Port pair you want to bind to, eg:

    var
      Binding: TIdSocketHandle
    
    Binding := IdUDPServer1.Bindings.Add;
    Binding.IP := ...;
    Binding.Port := ...;
    

    Or:

    with IdUDPServer1.Bindings.Add do
    begin
      IP := ...;
      Port := ...;
    end;
    

    If you set the DefaultPort property ahead of time, then you can simplify the above to this:

    IdUDPServer1.DefaultPort := ...;
    IdUDPServer1.Bindings.Add.IP := ...;
    

    With that said, socket error 10049 is WSAEADDRNOTAVAIL, which means you are using the wrong IP address in the first place. You need to specify an IP that belongs to the PC that TIdUDPServer is running on - the IP that the device will be sending UDP packets to. You can either bind to the single wildcard IP 0.0.0.0 (or just a blank string ''), which tells TIdUDPServer to bind to all available local IPs:

    IdUDPServer1.DefaultPort := ...;
    IdUDPServer1.Bindings.Add.IP := ''; // or: '0.0.0.0' 
    

    Or you can use Indy's GStack.LocalAddresses property to determine the locally available IPs and create separate Bindings for them individually as needed.

    IdUDPServer1.Bindings.Clear;
    IdUDPServer1.DefaultPort := ...;
    
    with GStack.LocalAddresses do
    begin
      for I := 0 to Count-1 do
        IdUDPServer1.Bindings.Add.IP := Strings[I];
    end;
    

    Update: if you bind the server to port 0 to let the OS pick a port, you can discover what port was selected by looking at the TIdSocketHandle.Port property after the server was activated:

    var
      Binding: TIdSocketHandle
      ListeningPort: TIdPort;
    
    IdUDPServer1.Bindings.Clear;
    
    Binding := IdUDPServer1.Bindings.Add;
    Binding.IP := ...;
    Binding.Port := 0;
    {
    or: 
    IdUDPServer1.DefaultPort := 0;
    Binding := IdUDPServer1.Bindings.Add;
    Binding.IP := ...;
    }
    
    IdUDPServer1.Active := True;
    
    ListeningPort := Binding.Port;