-1

PC の 2 つの空き TCP ポートで実行する必要がある 2 つのプロセスがあります。これは、ユーザーにとって簡単ですぐに使えるプロセスである必要があります。競合を回避するために空きポートを自動検出し、これらのポート番号をこれら 2 つのプロセスに適用したいと考えています。

これを実現するために、空きポートを検出する関数 (スレッドでも実行される) を作成しましたが、空きポートは見つかりません。誰かが私のコードの何が問題なのか説明できますか?

編集:「@ 500-error etc」によって提供される解決策がコードに適用されます。機能は正常に動作しています。

ここにあります:

    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;

上記の関数を呼び出すコード:

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;

私が間違っていることは何ですか?

注:コードをデバッグしました。プロセスは問題ないようです(テストを試みますが、例外はありません)。また、他のアプリでテストして、指定されたポートが使用されていないことを確認しました。

4

2 に答える 2

3

私は(今)問題はあなたが からの結果を誤解していることだと信じていますconnect

成功した場合connect(0 を返す)、ポートが使用中であることを意味します。

于 2013-05-16T23:09:12.310 に答える
2

あなたはこれを間違った方法で回避しました。bind()の代わりに使用する必要がありconnect()ます。ポートがすでに使用されている場合、bind()失敗します。connect()別の IPを試みる必要はありません。例えば:

uses
  winsock;

type
  TAvailablePortArray = array of Word;

function findAvailableTCPPort( const ipAddressStr : AnsiString; portStart : Word = 8080; portEnd : Word = 8084; findCount : Byte = 2 ) : TAvailablePortArray;
var
  client    : sockaddr_in;
  sock      : TSocket;
  wsdata    : WSAData;
  dwPort    : Word;
  iFound    : Byte;
  bResult   : Boolean;
  arrFound  : TAvailablePortArray;
begin
  SetLength( Result, 0 );
  if ( portStart = 0 ) or ( portStart > portEnd ) or ( findCount = 0 ) then
    Exit;

  //initiates use of the Winsock DLL
  if ( WSAStartup(MAKEWORD(2, 0), wsdata) <> 0 ) then
    Exit;

  try
    //Set the protocol to use , in this case (IPv4)
    fillChar( client, sizeOf( client ), 0 );
    client.sin_family      := AF_INET;
    client.sin_addr.s_addr := inet_addr(PAnsiChar(ipAddressStr));

    dwPort := portStart;
    SetLength( arrFound, findCount );
    try
      iFound := 0;

      repeat
        sock := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    //creates a socket
        if sock = INVALID_SOCKET then Break;

        try
          if GetQueueStatus(QS_ALLINPUT) <> 0 then
            Application.ProcessMessages();

          client.sin_port := htons(dwPort); //convert to TCP/IP network byte order (big-endian)

          if bind(sock, PSockAddr(@client)^, sizeOf(client)) = 0 then
          begin
            arrFound[iFound] := dwPort;
            Inc( iFound );
          end;
        finally
          closesocket(sock);
        end;

        Inc(dwPort);
      until ( iFound = findCount ) or ( dwPort > portEnd );
    finally
      SetLength(arrFound, iFound);
    end;
  finally
    WSACleanup();
  end;

  Result := arrFound;
end;

または、ソケットの使用をまったく忘れてください。代わりに、使用中のアクティブなポートを一覧表示する Windows の TCP/UDP テーブルを列挙します。GetTcpTable()GetTcp6Table()GetUdpTable()、および を見てくださいGetUdp6Table()

そうは言っても、どちらのアプローチにも根本的な欠陥があります。これは、どちらのアプローチにも競合状態があるためです。関数が使用可能なポートを見つけた後、他の誰かがやって来て、プロセスができる前にポートを開く可能性があります。最良のオプションは、各プロセスbind()自体を無条件にポート 0 に設定し、その時点で OS に使用可能なポートを選択させることです。その後、必要に応じて 2 つのプロセスが指定されたポートをアナウンスできます。

于 2013-05-17T01:42:45.813 に答える