Search code examples
delphiindy10

How do I distinguish connections from multiple clients with the same IP address?


What is the proper way to identify clients if they have the same IP and ports? If they are only connected via LAN, e.g. ip: 198.162.1.1 port: 2015. How do I detect which client has disconnected using its unique ID if they have the same IP?

TClient = class(TIdServerContext)
  private
  public
    PeerIP      : String;     
    procedure SendMessage(cIP, mStr : String);
  end;

procedure TClient.SendMessage(cIP, mStr : String);
var
  Context: TClient;
  List: TList;
  I: Integer;
begin
  List := Form1.IdTCPServer1.Contexts.LockList;
  try
    for I := 0 to List.Count-1 do
    begin
      Context := TClient(List[I]);
      if (Context.PeerIP = cIP) then
      begin
        Connection.IOHandler.WriteLn(mStr);
        Break;
      end
    end;
  finally
    Form1.IdTCPServer1.Contexts.UnlockList;
  end;
end;

I'm only storing the client IP and using it as an ID.

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
begin
  with TClient(AContext) do
  begin
    if AContext.Connection.Connected then
    begin
      PeerIP := Connection.Socket.Binding.PeerIP;
    end;
  end;
end;

Maybe like ClientID := Connection.Socket.Binding.Handle;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
 //Connection.Socket.Binding.Handle; ?? 
end;

Solution

  • The PeerIP alone is not unique enough to identify a client. Think of what happens when multiple clients running on one side of a router connect to the same server running on the other side of the router. The clients will have the same PeerIP (the router's IP) from the server's perspective. You need each client's PeerIP and PeerPort together.

      TClient = class(TIdServerContext)
      public
        PeerIP      : String;
        PeerPort    : TIdPort;
        procedure SendMessage(cIP: string; cPort: TIdPort; mStr : String);
      end;
    
    procedure TClient.SendMessage(cIP: string; cPort: TIdPort; mStr : String);
    var
      Context: TClient;
      List: TList;
      I: Integer;
    begin
      List := Form1.IdTCPServer1.Contexts.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          Context := TClient(List[I]);
          if (Context <> Self) and (Context.PeerIP = cIP) and (Context.PeerPort = cPort) then
          begin
            Context.Connection.IOHandler.WriteLn(mStr);
            Break;
          end
        end;
      finally
        Form1.IdTCPServer1.Contexts.UnlockList;
      end;
    end;
    
    procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
    begin
      with TClient(AContext) do
      begin
        PeerIP := Connection.Socket.Binding.PeerIP;
        PeerPort := Connection.Socket.Binding.PeerPort;
      end;
    end;
    
    procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
    begin
     ...
    end;
    

    Or simply don't rely on IP/Port at all. Make up your own unique ID, such as requiring each client to login to the server with a UserID.

      TClient = class(TIdServerContext)
      public
        UserID      : String;
        procedure SendMessage(cUser, mStr : String);
      end;
    
    procedure TClient.SendMessage(cUser, mStr : String);
    var
      Context: TClient;
      List: TList;
      I: Integer;
    begin
      List := Form1.IdTCPServer1.Contexts.LockList;
      try
        for I := 0 to List.Count-1 do
        begin
          Context := TClient(List[I]);
          if (Context <> Self) and (Context.UserID = cUser) then
          begin
            Context.Connection.IOHandler.WriteLn(mStr);
            Break;
          end
        end;
      finally
        Form1.IdTCPServer1.Contexts.UnlockList;
      end;
    end;
    
    procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
    begin
      with TClient(AContext) do
      begin
        // this is just for demonstration. Obviously, you
        // should implement a real protocol with authentication,
        // duplicate login detection, etc...
        UserID := Connection.IOHandler.ReadLn;
      end;
    end;
    
    procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
    begin
     ...
    end;