Search code examples
windowsdelphisocketsports

Delphi: Try to find open TCP ports but doesn't detect any open port


I have two processes that must run on two free TCP ports on the PC. This must be a painless out-of-the-box process for it's user, I want to auto detect free ports to avoid conflicts and apply these port numbers to these two processes.

To achieve this, I made a function (that runs in a thread also) to detect the free ports but doesn't find any free ports. Can somebody explain what is wrong with my code?

EDIT: The solution provided by "@500-error etc" is applied to the code. Function now working OK.

Here it is:

    uses
     winsock;

    type
     TAvailablePortArray = array of Word;

function findAvailableTCPPort( ipAddressStr : String; portStart : Word = 8080; portEnd : Word = 8084; findCount : Byte = 2 ) : TAvailablePortArray;
var
  client    : sockaddr_in;
  sock      : Integer;
  ret       : Integer;
  wsdata    : WSAData;
  dwPort    : Word;
  iFound    : Byte;
  bResult   : Boolean;
  bAllFound : Boolean;
  dns       : PHostEnt;
  status    : LongInt;


begin
 setLength( Result, 0 );
 if( portStart > portEnd ) or ( portStart = 0 ) or ( findCount = 0 ) then
  Exit;

 try
 ret := WSAStartup($0002, wsdata); //initiates use of the Winsock DLL
 except
  ret:=-1;
 end;

 if( ret <> 0 ) then
  Exit;

 dns:=getHostByName( PChar(ipAddressStr) );
 if( NOT Assigned( dns )) then
  Exit;

 bResult:=TRUE;
 try
  fillChar( client, sizeOf( client ), 0 );
  client.sin_family      := AF_INET;  //Set the protocol to use , in this case (IPv4)
  client.sin_addr.s_addr :=LongInt(PLongInt(dns^.h_addr_list^)^);
  //inet_addr(PAnsiChar(ipAddressStr));  //convert to IN_ADDR  structure
 except
  bResult:=FALSE;
 end;

 if( bResult ) then
 begin
  dwPort:=portStart;
  setLength( Result, findCount );
  bAllFound:=FALSE;
  iFound:=0;

  while( NOT bAllFound ) and ( dwPort <= portEnd ) do
  begin
   try
    client.sin_port:=htons(dwPort); //convert to TCP/IP network byte order (big-endian)
    sock:=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );    //creates a socket
    Application.processMessages();
    status:=connect(sock,client,sizeOf(client));
    bResult:=(status <> 0); //establishes a connection to a specified socket, less than zero is NOT in use
   except
    bResult:=FALSE;
   end;

   if( sock <> 0 ) then
   begin
    closesocket(sock);
    sock:=0;
   end; 

   if( bResult ) then
   begin
    Result[iFound]:=dwPort;
    inc( iFound );
    bAllFound:=( iFound = findCount );
   end;

   inc(dwPort);
  end;
 end;

 if( NOT bAllFound ) then
  setLength( Result, 0 );

 try
  WSACleanup();
 except;
 end;
end;

Some code to call the function above:

procedure TForm1.btStartClick(Sender: TObject);
begin
 addLogMsg( 'Starting service ...' );
 FPorts:=findAvailableTCPPort( '127.0.0.1' );
 FPortCount:=Length( FPorts );
 addLogMsg( 'Available ports found: '+strToInt( FPortCount ));

 if( FPortCount < 2 ) then
 begin
  addLogMsg( 'ERROR: Cannot start service(s), insufficient free ports!' );
  Exit;
 end;
 ................
 ................
 ................
end;

What I am doing wrong?

NOTE: I have debug the code, the process seems to be OK (it tries to test it, no exceptions). Also verified that the ports specified are not in use by testing it with other app.


Solution

  • I (now) believe the issue is that you are misinterpreting the result from connect.

    If connect succeeds (returns zero), it means that the port is in use.